With find
, you can recursively list all files that match a certain criterion, e.g. the file name.
for file in $(find . -type f -name "*.csv"); do cat "$file" >> /path/to/output.csv; done
Breaking it up, find . -name "*.csv"
will find all CSV files from the current folder you're in (.
), and the loop will just iterate over that list, appending everything to the output.csv
file.
But: File names with spaces, globbing characters, and newlines can be tricky here. A safer solution would be to just use exec
for the find command.
find . -name "*.txt" -exec cat '{}' >> /path/to/output.csv ';'
Here, '{}'
will be replaced by find with the filename. For a long Q&A about why this is and how to circumvent the problem can be found here.
Now, if you want to create one CSV file for each directory – sorry, didn't see that before –, I'd probably do something like this:
for dir in $(find . -type d); do find $dir -maxdepth 1 -name "*.csv" -exec cat {} >> "$dir/out" ';'; mv "$dir/out" "$dir/merged.csv"; done
Although Franck's solution below is probably more efficient.
Of course, pay attention to the difference between >
and >>
. The former will always truncate the file to zero-length before writing to it, whereas the latter will just append to the file.
The reason why cat *.csv > merged.csv
worked—and why in your loop, it won't work—is that the shell will expand the wildcard before, so basically it sees:
cat file1.csv file2.csv file3.csv > merged.csv
… which will of course not overwrite anything.
$DIR
and$dir
are not the same thing.