
Я хочу объединить тысячи маленьких текстовых файлов в один большой текстовый файл. Они у меня в каталогах со структурой: timestamp1/status.txt
. Например: 20130430133144/status.txt
. Пока что я знаю, что
cat */* > bigtextfile.txt
работает для небольшого количества файлов. Но будет ли работать для большего количества? Интересно, cat
будет ли он собирать содержимое всех файлов, а затем пытаться сохранить в bigtextfile
. В противном случае, я полагаю, должен быть другой способ сделать это, например, извлечь один файл, добавить его в bigtextfile
, затем извлечь другой и так далее.
решение1
В:
cat */* > bigtextfile.txt
Оболочка расширится */*
до отсортированного списка (не скрытых) соответствующих файлов и выполнит команду cat
с указанием путей к этим файлам в качестве аргументов.
cat
будет открывать каждый файл по очереди и записывать на его стандартный вывод то, что он считывает из файла. cat
не будет хранить в памяти более одного буфера, заполненного данными (примерно несколько килобайт) за раз.
Проблема, с которой вы можете столкнуться, заключается в том, что список аргументов cat
настолько велик, что достигает предела размера аргументов системного execve()
вызова. Поэтому вам может потребоваться разделить этот список файлов и запустить его cat
несколько раз.
Для этого можно использовать xargs
(здесь с GNU или BSD xargs
для нестандартных -r
опций -0
):
printf '%s\0' */* | xargs -r0 cat -- > big-file.txt
(поскольку printf
встроен в оболочку, он не проходит через execve
системный вызов, а значит, и через его предел).
Или find
составьте список файлов и выполните столько команд cat, сколько необходимо:
find . -mindepth 2 -maxdepth 2 -type f -exec cat {} + > big-file.txt
Или переносимо:
find . -path './*/*' -prune -type f -exec cat {} + > big-file.txt
(однако следует помнить, что в отличие от */*
, он будет включать скрытые файлы (и файлы в скрытых каталогах), а не искать файлы в символических ссылках на каталоги, и список файлов не будет отсортирован).
Если вы используете последнюю версию Linux, вы можете снять ограничение на размер аргументов, выполнив:
ulimit -s unlimited
cat -- */* > big-file.txt
С zsh
, вы также можете использовать zargs
:
autoload zargs
zargs -- */* -- cat > big-file.txt
С помощью ksh93
вы можете использовать command -x
:
command -x cat -- */* > big-file.txt
Все они делают одно и то же: разбивают список файлов и запускают столько cat
команд, сколько необходимо.
Опять ksh93
же, обойти ограничение можно execve()
с помощью встроенной cat
команды:
command /opt/ast/bin/cat -- */* > big-file.txt
решение2
Нет cat
не будет буферизовать все файлы перед началом записи.
Однако если у вас большое количество файлов, вы можете столкнуться с проблемой с количеством переданных аргументов cat
. По умолчанию ядро Linux позволяет передавать любой программе только фиксированное количество аргументов (я не помню, как получить это значение, но в большинстве случаев это несколько тысяч).
Чтобы решить эту проблему, вы можете сделать что-то вроде этого:
find -mindepth 2 -maxdepth 2 -type f -exec cat {} \; > bigtextfile.txt
По сути, это будет вызываться cat
отдельно для каждого файла, найденного find
.
решение3
Если количество файлов слишком велико, то */*
будет выдан слишком большой список аргументов. Если так, то подойдет что-то вроде этого:
find . -name "*.txt" | xargs cat > outfile
(идея состоит в том, чтобы использовать find
для сбора имен файлов и преобразования их в поток; xargs
разбивает этот поток на управляемые части, чтобы передать их в cat
, который объединяет их в выходной поток xargs
, а тот отправляется в outfile
).