34

From the command line, what is the easiest way to show the contents of multiple files? My directory looks like below.

./WtCgikkCFHmmuXQXp0FkZjVrnJSU64Jb9WSyZ52b
./xdIwVHnHY7dnuM9zcPDYQGZFdoVORPyMVD2IzjgM
./GZnATXO1e5Hh3Bz1bhgJjjwheIjjZqtnXR0hfOyj
./mWz7ehBNoTZmtDh8JG6sxw2lMJFwIovPzxDGECUY
./JN65F5v3RL2ilHPqNSx9N9D4lvVpqpbJ9lASd8TJ
./At9PS4y4nTiXUO0Z0USnbYkTPBla1msQRpwuruqE
./YiPyMZPCaUDZTiTczAvWII9bJrUqLXCFtH2pXEA2
./JoakdlbRFPwAvWp1d4n8RvMoyMeizCoiriL2Sn2U
./wFPWZUus8Yu7UtESGABLCoqDg36cT90USO0xuyUr
./qseI9PgV1EJfZCDyGGeVytajqG7JeX0r7eA5S1JW
./zgFJpNgXyCsaVh38aCuMGuzHwIbwSNB6rQDdh27x
./.htaccess

Now I'd like to view the contents of all files except .htaccess. It might look something like:

WtCgikkCFHmmuXQXp0FkZjVrnJSU64Jb9WSyZ52b:
Contents of file WtCgikkCFHmmuXQXp0FkZjVrnJSU64Jb9WSyZ52b.

xdIwVHnHY7dnuM9zcPDYQGZFdoVORPyMVD2IzjgM:
Contents of file xdIwVHnHY7dnuM9zcPDYQGZFdoVORPyMVD2IzjgM.

[...]

I think this should be doable with a combination of find, xargs and cat, but I haven't figured out how. Thanks for your time!

1

5 Answers 5

49

The standard head command and some implementations of tail print a header with the file name if you pass them more than one file argument (POSIX tail accepts only 0 or 1 file argument). To print the whole file, use tail -n +1 (print from the first line onwards, i.e. everything).

Here, it looks like you want to see every file except the single one whose name begins with a dot. Dot files are “hidden” under unix: they don't appear in the default output of ls or in a wildcard match. So matching every non-hidden files is done with just *.

tail -n +1 -- *

Or if your tail can't take more than one argument:

head -n 999999 -- *

(some head -n -0 -- * but that's not standard either).

The GNU implementation of head/tail also accept a -v/--verbose option that ensures the header is printed even when only one filename is given.

-- is needed to cover the cases where one of the file names begins with a -. Beware a file called - would still be taken by head / tail as meaning stdin. Using ./* would work around it but would mean the ./ prefix would be included in the header on output.

3
  • Short and sweet, thank you! Do you know where I can read about the -- modifier?
    – Znarkus
    Apr 30, 2011 at 10:37
  • 2
    @Znarkus: -- to signify the end of options is a convention that most commands obey. E.g. tail -n +1 -- -f -g tells tail that -f and -g are files to read (operands) and not options. It's guideline 10 in the POSIX utility syntax guideline (a document intended for utility writers). I don't have a reading suggestion intended for end-users, I'd expect a good unix tutorial to cover it at some point. Apr 30, 2011 at 10:50
  • Just a remark, because many other answers propose to use find, but then use some exec with print formatting: A simple way to use find and tailis: find . -type f -print0 -name "*" | xargs -0 tail -n +1 -- (-print0 and xargs -0 and the -- ensure that the command also works with filenames with spaces or starting with -)
    – IanH
    Dec 8, 2019 at 10:41
25

You can do it all in one with find:

$ find . -type f -not -name .htaccess -printf "\n%p\n" -exec cat {} \;

That tells find to find all files (-type f) in the current directory (.) except (-not) one named .htaccess (-name .htaccess). Then it prints (-printf) a newline followed by the filename (%p), and then runs cat on the file (-exec cat {} \;). That will give you output like:

test/test3
Line 1

test/test2
Line 1

test/test1
Line 1
Line 2
Line 3

If you do this often it might be worth sticking it in a shell script or a function; I have one named cats that does exactly that:

#!/bin/bash
for filename; do
    echo "\033[32;1m$filename\033[0m"
    cat "$filename"
    echo
done

It loops over each filename argument, prints out the filename (in bold green), and then cats the file:

Example screenshot

So then the command would just be:

$ find . -type f -not -name .htaccess -exec cats {} \+
2
  • The other was shorter, so accepted that. Excellent answer though, thank you!
    – Znarkus
    Apr 30, 2011 at 10:37
  • -exec can be used multiple times, I find this one more readable/tweakable: find . -type f -exec echo === {} === \; -exec cat {} \;
    – mvorisek
    May 16, 2021 at 8:39
6

To show content of all files in the current folder, try:

grep -vI "\x00" -- *

and similar, but recursively:

grep -vIr "\x00" -- .

The format would be: filename: content.

To have similar format as suggested, it would be:

grep -rvl "\x00" -- * | while read file; do printf "\n\n#### $file ####\n"; cat $file; done

Side notes:

  • Using NUL (\x00) in above examples prevents displaying binary files (which -I is actually doing, but we've to still use some pattern).
  • Using wildcard (*), it automatically ignores hidden files such as .htaccess.

See also: grep: display filename once, then display context with line numbers at Unix SE

1
  • 2
    Usually grep -r ^ is enough.
    – catpnosis
    Apr 8, 2016 at 18:48
3

You can use this:

cat $(find Directory/ -not -name *.htaccess)
0

If you want too print it recursively use (for files with .sql extension, for example):

find -name "*.sql" -exec cat {} \; > all.sql

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