У меня есть несколько файлов (они генерируются случайным образом каждый раз), в имени которых есть номер - внутри файла номер повторяется. Пример:

file1_85.txt
file1_242.txt
file1_9.txt

Я хочу объединить содержимое этих файлов в один файл большего размера, file_all.txt.

Код, который я пробовал использовать, таков:

for f in file1_*.txt; do (cat "${f}"; echo " ") >> file_all.txt; done

Однако содержимое file_all.txt выглядит так:

file1_242.txt
file1_85.txt
file1_9.txt

Когда я действительно хочу, чтобы это выглядело так:

file1_9.txt
file1_85.txt
file1_242.txt

Что могло бы произойти, если бы bash упорядочил файлы по порядку номеров.

Я пробовал это:

for f in file1_{1..99999}.txt; do (cat "${f}"; echo " ") >> file_all.txt; done

Это сработало, однако я получал сообщения об ошибках «Нет такого файла или каталога», когда он проходил через номер, для которого не было подходящего файла. Кроме того, это требует очень много времени. Есть ли лучший способ выполнить эту задачу?

0
IcedCoffee 18 Апр 2016 в 18:42

3 ответа

Лучший ответ

Предполагая, что в именах файлов нет символов новой строки и у вас установлена версия sort GNU, это будет работать:

while read file; do 
   cat "$file"
   echo 
done < <(ls -1 file_*.txt | sort -V) > file_all.txt

Если ваш sort не поддерживает -V (как, например, OS X), вы можете использовать согласованность имени файла, чтобы вместо этого выполнить прямую числовую сортировку:

while read file; do 
   cat "$file"
   echo 
done < <(ls -1 file_*.txt | sort -t_ -n -k2,2) > file_all.txt

Наконец, если ваши файлы содержат символы новой строки, вы все равно можете использовать sort, но вам нужно использовать параметр -z в сочетании с другими инструментами, которые завершают элементы списка байтами NUL вместо символов новой строки:

find . -depth 1 -name 'file_*' -print0 | sort -zV | xargs -0 -I{} bash -c 'cat {}; echo'

Замените sort -zV на sort -z -t_ -n -k2,2 для более старой версии сортировки GNU, в которой отсутствует опция -V; однако сортировка, не относящаяся к GNU, вероятно, также не будет иметь -z.

4
Mark Reed 18 Апр 2016 в 15:56

Для имен файлов, потенциально содержащих символы новой строки:

$ find -name 'file1*' -print0 | sort -zV | xargs -0 cat
file1_9
file1_85
file1_242

Или, если опция -V недоступна,

$ find -name 'file1*' -print0 | sort -z -n -t '_' -k 2 | xargs -0 cat
file1_9
file1_85
file1_242

Это использует имена файлов, разделенных нулем; опция -z указывает sort ожидать (и производить) имена файлов, разделенных нулем, и xargs -0 также предназначен для ввода, разделенного нулем.

4
Benjamin W. 18 Апр 2016 в 15:56

Ваш метод «грубой силы» будет работать, если:

$ for f in file1_{1..99999}.txt; do [ -f "${f}" ] && cat "${f}" >> file_all.txt; done

Сравнение: [ -f "${f}" ] проверьте, существует ли файл до cat, избегая сообщения об ошибке.

1
Lacobus 18 Апр 2016 в 16:09