views:

2266

answers:

8

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?

+8  A: 

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
Commodore Jaeger
I'm on Darwin so that may be a caveat that should have been posted but with some of these methods, I'm getting a looping behavior where the contents of the files are continuously appended for infinity.
JR Lawhorne
yeah that's because out.txt is in the same directory as . ; put out.txt somewhere outside.
dlamblin
yep. That does the trick. The output file can't be in the same dir you find in.
JR Lawhorne
+1  A: 

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
Jay
A: 

You could do something like this :

$ cat `find . -type f` > out.txt
JoMo
This one worked for me on Darwin.
JR Lawhorne
This works as long as find does not find too many files. Use xargs to compensate.
Martin York
A: 

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 :-)

Zsolt Botykai
+2  A: 

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
+1  A: 

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.

dlamblin
Writing the output file to the same directory was defiantly a problem. Many of the solutions work if you avoid doing that.
JR Lawhorne
A: 

Try this:

(find . -type f -exec cat {} \;) > out.txt
Milan Babuškov
If you change this to write the output file to a different directory from where you're finding, it works.
JR Lawhorne
A: 

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

ljorquera