JR Lawhorne
JR Lawhorne

Reputation: 3302

How do I concatenate files in a subdirectory with Unix find execute and cat into a single file?

I can do this:

$ find .
.
./b
./b/foo
./c
./c/foo

And this:

$ find . -type f -exec cat {} \;
This is in b.
This is in c.

But not this:

$ find . -type f -exec cat > out.txt {} \;

Why not?

Upvotes: 10

Views: 22737

Answers (8)

dlamblin
dlamblin

Reputation: 45331

Maybe you've inferred from the other responses that the > symbol is interpreted by the shell before find gets it as an argument. But to answer your "why not" lets look at your command, which is:

$ find . -type f -exec cat > out.txt {} \;

So you're giving find these arguments: "." "-type" "f" "-exec" "cat" you're giving the redirect these arguments: "out.txt" "{}" and ";". This confuses find by not terminating the -exec arguments with a semi-colon and by not using the file name as an argument ("{}"), it possibly confuses the redirection too.

Looking at the other suggestions you should really avoid creating the output in the same directory you're finding in. But they'd work with that in mind. And the -print0 | xargs -0 combination is greatly useful. What you wanted to type was probably more like:

$ find . -type f -exec cat \{} \; > /tmp/out.txt

Now if you really only have one level of sub directories and only normal files, you can do something silly and simple like this:

cat `ls -p|sed 's/\/$/\/*/'` > /tmp/out.txt

Which gets ls to list all your files and directories appending '/' to the directories, while sed will append a '*' to the directories. The shell will then interpret this list and expand the globs. Assuming that doesn't result in too many files for the shell to handle, these will all be passed as arguments to cat, and the output will be written to out.txt.

Upvotes: 1

ljorquera
ljorquera

Reputation: 1080

In bash you could do

cat $(find . -type f) > out.txt

with $( ) you can get the output from a command and pass it to another

Upvotes: 0

Milan Babuškov
Milan Babuškov

Reputation: 61138

Try this:

(find . -type f -exec cat {} \;) > out.txt 

Upvotes: 0

Johnny A
Johnny A

Reputation:

Hmm... find seems to be recursing as you output out.txt to the current directory

Try something like

find . -type f -exec cat {} \; > ../out.txt

Upvotes: 5

Zsolt Botykai
Zsolt Botykai

Reputation: 51613

Or just leave out the find which is useless if you use the really great Z shell (zsh), and you can do this:

setopt extendedglob

(this should be in your .zshrc) Then:

cat **/*(.) > outfile 

just works :-)

Upvotes: 0

JoMo
JoMo

Reputation: 436

You could do something like this :

$ cat `find . -type f` > out.txt

Upvotes: 3

Jay
Jay

Reputation: 42642

How about just redirecting the output of find into a file, since all you're wanting to do is cat all the files into one large file:

find . -type f -exec cat {} \; > /tmp/out.txt

Upvotes: 2

Commodore Jaeger
Commodore Jaeger

Reputation: 33390

find's -exec argument runs the command you specify once for each file it finds. Try:

$ find . -type f -exec cat {} \; > out.txt

or:

$ find . -type f | xargs cat > out.txt

xargs converts its standard input into command-line arguments for the command you specify. If you're worried about embedded spaces in filenames, try:

$ find . -type f -print0 | xargs -0 cat > out.txt

Upvotes: 30

Related Questions