8

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?

12

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.

  • It seems rather obvious to use iteration in hindsight! I thought there might have been a flag in cat, but this works too! Thank you. – lennyklb Oct 14 '15 at 15:24
  • You don't need the in "$@" - it's implicit. – l0b0 Oct 14 '15 at 23:05
  • 4
    being explicit is good for readability. – cas Oct 15 '15 at 0:12
5

Just use a loop instead of simple cat:

for file in /path/to/targetdir/*; do
    echo "-------- $file -------"
    cat "$file" 
done
  • 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
3

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)

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.

2

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 *
  • @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
  • 1
    @DennisWilliamson: Using \n in RHS isn't standard. You can use sed -e '$G'. – cuonglm Oct 15 '15 at 1:36
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
$ 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.