У меня есть папка с 30000 txt файлов, каждый файл 50-60кб. Мне нужно слить их в 2.5mb txt файлы. И удалить тот, который сливался. Мой код должен быть примерно таким: for f in *,50; do cat file1,file2...file49 > somefile.txt;done Конечно, это псевдокод. Мне нужно было бы объединить файлы в пакет из 50 файлов, а затем удалить использованный. Кто-нибудь может мне помочь?

0
K.Mazur 19 Авг 2021 в 10:53
1. Удаление и повторное размещение одного и того же вопроса вам не поможет. 2. Объедините все файлы в один файл, а затем разделите его с помощью split -c 2500000. 3. Вы говорите: «Мой код должен быть примерно таким», но на самом деле нет, вы только думаете, что он должен быть таким. Это было плохое решение для вашего последнего вопроса и ужасное решение для этого почти идентичного. Ваша «потребность» является примером Проблемы XY.
 – 
cas
19 Авг 2021 в 11:00
Мне пришлось удалить вопрос, что мне сказал сделать stackexchange. Он связан с моим другим вопросом и сказал, что он был продублирован, но это не так. Но создание split -c 2500000 привело бы к созданию файлов txt с отсутствующими символами или неполными предложениями... теперь у меня всего 30 000 файлов txt, поэтому для меня было бы лучше добавить 1..49,50..99.100. .149 .... и т. д., чем создание большого файла, а затем разделение.
 – 
K.Mazur
19 Авг 2021 в 11:09
Я видел много-много ответов перед публикацией квеста. что-то вроде этого, я получаю каждый 50-й файл for file in `find folder -type f | awk 'NR %50 == 0'`; do echo $file;done , но как мне теперь добавить из $file все файлы до следующего $file?
 – 
K.Mazur
19 Авг 2021 в 11:21
1
Да ладно, не надо так резко. Вы не знаете, что является более подходящим критерием: «2,5 М» или «50 файлов». Вы, кажется, просто предположили, что ОП по какой-то причине хочет 2,5 млн. Возможно, что действительно заботит ОП, так это то, что каждый составной файл содержит содержимое ровно 50 оригиналов, и размер не имеет значения, так что это вы, а не ОП, отвлекаете себя. К. Мазур, пожалуйста, отредактируйте свой вопрос и уточните, что вы хотите. Вам нужно ровно 2,5 М данных на файл или ровно 50 файлов на объединенный файл? Или, может быть, вам нужно не более 2,5 на файл?
 – 
terdon
19 Авг 2021 в 11:42

2 ответа

С zsh:

files=( ./input-file*(Nn.) )
typeset -Z3 n=1
while
 (( $#files > 0 )) &&
   cat $files[1,50] > merged-file$n.txt &&
   rm -f $files[1,50]
do
  files[1,50]=()
  ((n++))
done

Там ./input-file*(Nn.) расширяется до файлов, которые соответствуют ./input-file*, но с 3 квалификаторами glob дополнительно классифицируют это:

  • N: nullglob: заставляет глобус расширяться до нуля вместо прерывания с ошибкой, когда совпадений нет. Это то, что вам часто нужно при установке массива из глобуса, и это нормально, если этот массив в конце будет пустым:
  • n: numericglobsort: изменение сортировки по умолчанию с лексической на числовую (фактически комбинация обоих), так что, например, input-file2 сортируется до input-file10.
  • .: ограничить обычными файлами (игнорировать каталоги, символические ссылки, FIFO...)

typeset -Z3 n делает $n переменной, дополненной нулями до ширины 3, поэтому мы получаем merged-file001.txt, ... merged-file049.txt...

Затем мы зацикливаемся до тех пор, пока в массиве $files есть элементы и нет ошибок, объединяя пакеты по 50 за раз (и все, что осталось для последнего пакета).

То же самое с инструментами bash 4.4+ и GNU:

readarray -td '' files < <(
  LC_ALL=C find . -maxdepth 1 -name 'input-file*' -type f -print0 |
    sort -zV
)
n=0
set -- "${files[@]}"
while
 (( $# > 0 )) &&
   printf -v padded_n %03d "$n" &&
   cat "${@:0:50}" > "merged-file$padded_n.txt" &&
   rm -f "${@:0:50}"
do
  shift "$(( $# >= 50 ? 50 : $# ))"
  ((n++))
done

Где find выполняет работу ./input-file*(N.) zsh, sort -V выполняет числовую (версию) сортировку, и мы используем позиционные параметры и shift в цикле как bash массивы весьма ограничены.

2
Stéphane Chazelas 19 Авг 2021 в 18:55
Зачем вам использовать rm -f? Кажется, что это просто добавляет (небольшой) риск без всякой пользы. Если что-то защищено от записи, может быть, его не следует удалять?
 – 
terdon
19 Авг 2021 в 13:46
@terdon, rm без -f предназначен для интерактивного использования, обычно вам это не нужно в сценариях. Здесь спецификация ясна, что файлы, которые были объединены, должны быть удалены.
 – 
Stéphane Chazelas
19 Авг 2021 в 13:52
Если кот выйдет из строя на полпути и в архив были добавлены какие-то файлы, ни один файл не будет стерт.
 – 
ImHere
19 Авг 2021 в 20:29
@ImHere, да, и n и $files останутся как есть, так что вы можете исследовать и исправить проблему и в этом случае перезапустить оттуда. Я подумал, что это, вероятно, лучший подход.
 – 
Stéphane Chazelas
19 Авг 2021 в 20:33
Почему вам нужно установить локаль C для поиска. Он не сообщает обо всех файлах?
 – 
ImHere
19 Авг 2021 в 20:50

Этот сценарий:

  1. Для bash (как помечено),
  2. Избегание поиска (который не работает с недопустимыми символами),
  3. Убедившись, что обрабатываются только простые файлы (без каталогов),
  4. Использование sort для численной сортировки (ну, по версии) и
  5. Объединение k файлов (переменное количество)
  6. Удаление по одному файлу за раз (избегайте копирования блока файлов, которые не будут стерты)
dir="myDir"

readarray -td $'\0' files < <(
   for f in ./"$dir"/in-file*; do
       if [[ -f "$f" ]]; then printf '%s\0' "$f"; fi
   done |
       sort -zV
)

k=50
rm -f ./"$dir"/joined-files*.txt
for i in "${!files[@]}"; do
   n=$((i/k+1))
   cat "${files[i]}"  >> ./"$dir"/joined-files$n.txt &&
       rm -f "${files[i]}"
done

0
ImHere 19 Авг 2021 в 23:12