11

I want to use cat with wildcards in bash in order to print multiple small files (every file is one sentence) to standard output. However, the separate file contents are not separated by a newline, which I'd like to for ease of reading.

How can I add a file delimiter of some sort to this command?

lennyklb
  • 213

6 Answers6

13

Define a shell function which outputs an end of line after every file and use it instead of cat:

endlcat() {
  for file in "$@"; do
    cat -- "$file"
    echo
  done
}

then you can use endlcat *.

The for loop loops over all provided arguments ($@) which are already escaped by the shell when you use wildcards like *. The -- is required to not choke on file names starting with a dash. Finally the echo outputs a newline.

Marco
  • 33,548
7

Just use a loop instead of simple cat:

for file in /path/to/targetdir/*; do
    echo "-------- $file -------"
    cat "$file" 
done
glenn jackman
  • 85,964
terdon
  • 242,166
  • Works like the accepted answer, but that one also had the next step to the commandline so I accepted the other one. – lennyklb Oct 14 '15 at 15:25
4

If you want file name between files, using tail:

tail -n +1 ./*

(In case you want tac instead of cat, some tail implementation have -r option for equivalent tac functional)

cuonglm
  • 153,898
4

This will work:

sed '' -- *

You don't need any shell loops or otherwise, and this will always follow the output of a file with a \newline - excepting perhaps the very last.

Otherwise if you want a blank line to be output between the output of each file, this could work:

bpaste(){
    eval "paste -'sd\n' -- $(x=0;for f do printf "\"\${$((x+=1))}\" - ";done)"
}   </dev/null

That will always follow the output of a file with a \newline and an additional blank line.

You can call it like:

bpaste *
mikeserv
  • 58,310
  • @cuonglm - the op wants a newline between files. this ensures a newline between files. – mikeserv Oct 14 '15 at 17:02
  • @cuonglm - you should try again. for n in 1 2 3; do printf "$n" >"$n"; done; sed '' -- [123] – mikeserv Oct 14 '15 at 17:18
  • @cuonglm - any sed must always print a \newline before it prints anything else, the last line of the last file might not get a \newline appended though. i could swear ive answered this question before. – mikeserv Oct 14 '15 at 17:26
  • @cuonglm - i dont understand what you mean. that little bit of shell i just gave you prints 1\n2\n3. it works. it always does. what does it print for you? – mikeserv Oct 14 '15 at 17:33
  • @cuonglm - thats not the question. the op said theres no newline between files he/she cats together - presumably because they dont end with a \newline. so this just adds one if one doesn't already exist. – mikeserv Oct 14 '15 at 17:38
  • @cuonglm - that wouldn't do a blank line if the files didnt end in newlines. – mikeserv Oct 14 '15 at 17:42
  • @cuonglm - look - printf 1|{ cat; echo; } - no blank line. if the input files dont end in \newlines this solution works exactly the same as the accepted answer, only it doesn't need to call cat once per file and doesnt do any unnecessary shell loops. if the op wants a blank line between the contents of each file he/she certainly didnt indicate it. – mikeserv Oct 14 '15 at 17:46
  • Right, maybe we need to ask the OP for clarity. – cuonglm Oct 14 '15 at 17:49
  • "I could swear I've answered this question before" - not the same Q but a very similar one: Join multiple files with two blank lines? – don_crissti Oct 14 '15 at 18:12
  • @don_crissti - no. that wasnt the one i was thinking of, but i guess i answered that, too. – mikeserv Oct 14 '15 at 18:25
  • @don_crissti - this is the one i was thinking of. its in the link list on the side, too. – mikeserv Oct 14 '15 at 18:27
  • sed '$s/$/\n/' -- * will replace the end of the last line of the last file (which might be a newline or it might be nothing) with a newline. Otherwise, it acts like the sed command in the answer. Similarly, awk '1' -- * does the same thing (the 1 is "true" which causes a print). If you want an extra newline between each file and you have GAWK 4: awk '{print} ENDFILE {print ""}' -- * – Dennis Williamson Oct 14 '15 at 22:38
  • 1
    @DennisWilliamson: Using \n in RHS isn't standard. You can use sed -e '$G'. – cuonglm Oct 15 '15 at 01:36
2

Similar to @terdon's answer, if you happen to prefer seeing the filename in the future as well, you can use the head command:

$ head *file*
==> file_1 <==
file 1 content

==> file_2 <==
file 2 content

==> file_3 <==
file 3 content

head defaults to first 10 lines, so using it without command options for your case (one sentence per file) is perfectly fine. Otherwise, you need the -n X option.

h.j.k.
  • 1,253
2

Another way - use grep -h to simply search for the empty string in each file. This will match all lines, regardless of how many or whether newline-terminated or not. grep results are always newline-terminated. The -h option suppresses prefixing each line of output with the filename it came from:

$ printf 'a' > a
$ printf 'b\nB' > b
$ printf 'c\n' > c
$ ls
a  b  c
$ cat -- *
ab
Bc
$ grep -h '' *
a
b
B
c
$ 

Or you could use GNU paste in -serial mode with newline as the delimiter:

$ paste -s -d '\n' -- *
a
b
B
c
$