Eu tenho cerca de 15.000 arquivos nomeados file_1.pdb
, file_2.pdb
, etc. Posso agrupar alguns milhares deles fazendo:
cat file_{1..2000}.pdb >> file_all.pdb
No entanto, se eu fizer isso para 15.000 arquivos, recebo o erro
-bash: /bin/cat: Argument list too long
Eu vi esse problema sendo resolvido fazendo, find . -name xx -exec xx
mas isso não preservaria a ordem com a qual os arquivos são unidos. Como posso conseguir isso?
Usando
find
,sort
exargs
:O
find
comando encontra todos os arquivos relevantes e, em seguida, imprime seus nomes de caminho parasort
fazer uma "classificação de versão" para colocá-los na ordem correta (se os números nos nomes de arquivo tivessem sido preenchidos com zero em uma largura fixa, não precisaríamos-V
).xargs
pega essa lista de nomes de caminhos classificados e os executacat
em lotes tão grandes quanto possível.Isso deve funcionar mesmo se os nomes dos arquivos contiverem caracteres estranhos, como novas linhas e espaços. Usamos
-print0
withfind
para fornecersort
nomes terminados em nul para classificar esort
os manipulamos usando-z
.xargs
também lê nomes terminados em nul com seu-0
sinalizador.Observe que estou escrevendo o resultado em um arquivo cujo nome não corresponde ao padrão
file_*.pdb
.A solução acima usa alguns sinalizadores não padrão para alguns utilitários. Eles são suportados pela implementação GNU desses utilitários e pelo menos pelo OpenBSD e pela implementação do macOS.
Os sinalizadores não padronizados usados são
-maxdepth 1
, parafind
inserir apenas o diretório superior, mas nenhum subdiretório. Positivamente, usefind . ! -name . -prune ...
-print0
, para criarfind
nomes de caminho com terminação nula (isso foi considerado pelo POSIX, mas rejeitado). Pode-se usar-exec printf '%s\0' {} +
em vez disso.-z
, para fazersort
registros com terminação nula. Não há equivalência POSIX.-V
, parasort
classificar, por exemplo ,200
após3
. Não há equivalência POSIX, mas pode ser substituído por uma classificação numérica em partes específicas do nome do arquivo se os nomes dos arquivos tiverem um prefixo fixo.-0
, para fazerxargs
registros lidos com terminação nula. Não há equivalência POSIX. Positivamente, seria necessário citar os nomes dos arquivos em um formato reconhecido porxargs
.Se os nomes de caminho forem bem comportados e se a estrutura do diretório for plana (sem subdiretórios), pode-se fazer sem esses sinalizadores, exceto
-V
comsort
.Com
zsh
(de onde{1..15000}
vem esse operador):Ou para todos os
file_<digits>.pdb
arquivos em ordem numérica:(onde
<x-y>
é um operador glob que corresponde a números decimais x a y. Com nenhumx
nemy
, é qualquer número decimal. Equivalente aextendedglob
's[0-9]##
oukshglob
's+([0-9])
(um ou mais dígitos)).Com
ksh93
, usando seucat
comando interno (portanto, não afetado por esse limite daexecve()
chamada do sistema, pois não há execução ):Com
bash
/zsh
/ksh93
(que suportamzsh
e{x..y}
possuemprintf
built-in):Em um sistema GNU ou compatível, você também pode usar
seq
:Para as
xargs
soluções baseadas em -, deve-se tomar cuidado especial com nomes de arquivo que contenham espaços em branco, aspas simples ou duplas ou barras invertidas.Como para
-It's a trickier filename - 12.pdb
, use:Um loop for é possível e muito simples.
A desvantagem é que você invoca
cat
um monte de vezes. Mas se você não consegue se lembrar exatamente como fazer as coisasfind
e a sobrecarga de invocação não é tão ruim em sua situação, vale a pena ter isso em mente.Premissa
Você não deve incorrer nesse erro apenas para arquivos de 15k com esse formato de nome específico [ 1 , 2 ] .
Se você estiver executando essa expansão de outro diretório e tiver que adicionar o caminho para cada arquivo, o tamanho do seu comando será maior e é claro que isso pode ocorrer.
Solução , execute o comando desse diretório.
Melhor solução Se, em vez disso, eu adivinhei mal e você o executa a partir do diretório em que os arquivos estão ...
IMHO, a melhor solução é a de Stéphane Chazelas :
com printf ou seq; testado em arquivos de 15k com apenas seu número dentro do pré-cache, é ainda o mais rápido (no momento e exceto o OP do mesmo diretório em que os arquivos estão).
Algumas palavras mais
Você deve ser capaz de passar para as linhas de comando do shell por mais tempo.
Sua linha de comando tem 213.914 caracteres e contém 15.003 palavras
cat file_{1..15000}.pdb " > file_all.pdb" | wc
...mesmo adicionando 8 bytes para cada palavra é 333.938 bytes (0,3M) muito abaixo do 2097142 (2,1M) relatado por
ARG_MAX
em um kernel 3.13.0 ou o ligeiramente menor 2088232 relatado como "Comprimento máximo do comando que poderíamos realmente usar" porxargs --show-limits
Dê uma olhada em seu sistema para a saída de
Solução guiada por preguiça
Em casos como este prefiro trabalhar com blocos até porque costuma sair uma solução eficiente em termos de tempo.
A lógica (se houver) é que estou com preguiça de escrever 1...1000 1001..2000 etc etc...
Então peço a um script que faça isso para mim.
Somente depois de verificar se a saída está correta, redireciono-a para um script.
... mas a preguiça é um estado de espírito .
Como sou alérgico a
xargs
(realmente deveria ter usadoxargs
aqui) e não quero verificar como usar, pontualmente termino de reinventar a roda como nos exemplos abaixo (tl;dr).Observe que, como os nomes dos arquivos são controlados (sem espaços, novas linhas...), você pode usar facilmente algo como o script abaixo.
tl;dr
Versão 1: passe como parâmetro opcional o 1º número do arquivo, o último, o tamanho do bloco, o arquivo de saída
Versão 2
Chamando o bash para a expansão (um pouco mais lento nos meus testes ~ 20%).
Claro que você pode ir em frente e se livrar completamente de
seq
[ 3 ] (do coreutils) e trabalhar diretamente com as variáveis no bash, ou usar python, ou compilar um programa ac para fazer isso [ 4 ] ...Outra forma de fazer pode ser