1

У меня есть большая серия подпапок на моем Mac со случайным количеством файлов CSV в каждом. Я хотел бы объединить эти файлы в один файл для каждого каталога.

До сих пор я знаю, что могу объединить эти файлы с cat * > mergedfile.csv , но у меня возникают проблемы с перебором всех папок. До сих пор мне удавалось объединить все виды вещей, но я не могу заставить это делать то, что я хочу точно.

Есть идеи, как лучше всего это сделать?

for DIR in ./subfolder/*
do
    cat $dir/* > merged.csv 
done

3 ответа3

3

С помощью find вы можете рекурсивно перечислить все файлы, которые соответствуют определенному критерию, например, имя файла.

for file in $(find . -type f -name "*.csv"); do cat "$file" >> /path/to/output.csv; done

Разбейте его, find . -name "*.csv" найдет все файлы CSV из текущей папки, в которой вы находитесь (.), и цикл будет просто перебирать этот список, добавляя все в файл output.csv .

Но: Имена файлов с пробелами, заглушающими символами и символами новой строки здесь могут быть сложными. Более безопасным решением было бы просто использовать exec для команды find.

find . -name "*.txt" -exec cat '{}' >> /path/to/output.csv ';'

Здесь '{}' будет заменено на find с именем файла. Длинные вопросы и ответы о том, почему это так и как обойти проблему, можно найти здесь.

Теперь, если вы хотите создать по одному CSV-файлу для каждого каталога - извините, раньше этого не видели - я бы, вероятно, сделал что-то вроде этого:

for dir in $(find . -type d); do find $dir -maxdepth 1 -name "*.csv" -exec cat {} >> "$dir/out" ';'; mv "$dir/out" "$dir/merged.csv"; done

Хотя приведенное ниже решение Франка, вероятно, более эффективно.


Конечно, обратите внимание на разницу между > и >> . Первый всегда обрезает файл до нулевой длины перед записью в него, тогда как второй просто добавляет файл.

Причина, по которой cat *.csv > merged.csv сработала - и почему в вашем цикле это не сработает, - в том, что оболочка расширила подстановочный знак раньше, поэтому в основном она видит:

cat file1.csv file2.csv file3.csv > merged.csv

... что, конечно, ничего не перезаписывает.

1

Предполагая bash 4+ (проверьте с помощью bash --version), вы можете активировать globstar с помощью shopt -s globstar и рекурсивно просматривать все каталоги (и только каталоги - конечные / исключающие файлы) с помощью **/

for f in **/; do cat "$f"/*.csv > "$f"/merged.csv; done

Если вы действительно хотите использовать все файлы в каталоге, а не только те, которые заканчиваются на .csv , то

for f in **/; do cat "$f"/* > "$f"/merged.csv; done

Если вы хотите перейти только на один уровень, а не быть полностью рекурсивным, используйте */ вместо **/ .

Ключевая ошибка в OP-скрипте (не говоря уже о том, что bash чувствителен к регистру) заключается в том, что он пытается записать содержимое всех файлов в один файл .csv и делает это так, как каждая итерация цикла. переписал бы последний.

Если вы хотите рекурсивно объединить все файлы .csv в один файл, вы можете снова использовать globstar

for f in **/*.csv; do cat "$f" > merged_all.csv
1

В родительскую папку:

for dir in $(find . -type d); do
  cd $dir
  [[ $(ls *.csv|wc -l) -eq 0 ]] 2> /dev/null || { print "$dir.csv created";
                                                  cat *.csv > $dir.csv; }
  cd - > /dev/null
done

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .