
Quero combinar milhares de pequenos arquivos de texto em um grande arquivo de texto. Eu os tenho em diretórios com a estrutura: timestamp1/status.txt
. Por exemplo: 20130430133144/status.txt
. Até agora eu sei disso
cat */* > bigtextfile.txt
funciona para um pequeno número de arquivos. Mas funcionará para números mais altos? Gostaria de saber se cat
vai reunir o conteúdo de todos os arquivos e depois tentar salvar no formato bigtextfile
. Caso contrário, suponho que haja outra maneira de fazer isso, como buscar um arquivo, anexá-lo bigtextfile
, buscar outro e assim por diante.
Responder1
Em:
cat */* > bigtextfile.txt
O shell se expandirá */*
para a lista classificada de arquivos correspondentes (não ocultos) e será executado cat
com esses caminhos de arquivo como argumentos.
cat
abrirá cada arquivo por vez e escreverá em seu stdout o que lê do arquivo. cat
não armazenará mais de um buffer cheio de dados (algo como alguns quilobytes) por vez na memória.
Um problema que você pode encontrar é que a lista de argumentos cat
é tão grande que atinge o limite do tamanho dos argumentos da execve()
chamada do sistema. Portanto, pode ser necessário dividir essa lista de arquivos e executar cat
várias vezes.
Você poderia usar xargs
para isso (aqui com GNU ou BSD para opções xargs
não padrão ):-r
-0
printf '%s\0' */* | xargs -r0 cat -- > big-file.txt
(porque printf
é construído no shell, ele não passa pela execve
chamada do sistema, portanto não passa pelo seu limite).
Ou find
faça a lista de arquivos e execute quantos comandos cat forem necessários:
find . -mindepth 2 -maxdepth 2 -type f -exec cat {} + > big-file.txt
Ou portável:
find . -path './*/*' -prune -type f -exec cat {} + > big-file.txt
(cuidado, porém, ao contrário de */*
, ele incluirá arquivos ocultos (e arquivos em diretórios ocultos), não procurará arquivos em links simbólicos para diretórios e a lista de arquivos não será classificada).
Se estiver em uma versão recente do Linux, você pode aumentar o limite do tamanho dos argumentos fazendo:
ulimit -s unlimited
cat -- */* > big-file.txt
Com zsh
, você também pode usar zargs
:
autoload zargs
zargs -- */* -- cat > big-file.txt
Com ksh93
você pode usar command -x
:
command -x cat -- */* > big-file.txt
Todos fazem a mesma coisa, dividem a lista de arquivos e executam quantos cat
comandos forem necessários.
Novamente ksh93
, você pode contornar o execve()
limite usando o cat
comando interno:
command /opt/ast/bin/cat -- */* > big-file.txt
Responder2
Não, cat
não armazenará todos os arquivos em buffer antes de começar a gravar.
No entanto, se você tiver um grande número de arquivos, poderá ter problemas com o número de argumentos passados para cat
. Por padrão, o kernel do Linux permite apenas que um número fixo de argumentos seja passado para qualquer programa (não me lembro como obter o valor, mas são alguns milhares na maioria dos casos).
Para resolver esse problema, você pode fazer algo assim:
find -mindepth 2 -maxdepth 2 -type f -exec cat {} \; > bigtextfile.txt
Basicamente, isso chamará cat
separadamente cada arquivo encontrado por find
.
Responder3
Se o número de arquivos for muito grande, a */*
lista de argumentos será muito grande. Nesse caso, algo parecido servirá:
find . -name "*.txt" | xargs cat > outfile
(a idéia é usar find
para pegar os nomes dos arquivos e transformá-los em um fluxo; xargs
corta esse fluxo em pedaços gerenciáveis para fornecer a cat
, que os concatena no fluxo de saída de xargs
, e isso vai para outfile
).