'Erro na lista de argumentos' durante a cópia de um grande número de arquivos

11

Estou usando o seguinte comando:

\cp -uf /home/ftpuser1/public_html/ftparea/*.jpg /home/ftpuser2/public_html/ftparea/

E estou recebendo o erro:

-bash: /bin/cp: Argument list too long

Eu também tentei:

ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} /home/ftpuser2/public_html/ftparea/

Ainda tenho -bash: / bin / ls: Lista de argumentos muito longa

Alguma idéia?

    
por icelizard 19.08.2009 / 15:48

9 respostas

18

*. jpg se expande para uma lista maior do que o shell pode manipular. Tente isso ao invés

find  /home/ftpuser/public_html/ftparea/ -name "*.jpg" -exec cp -uf "{}" /your/destination \;
    
por 19.08.2009 / 15:57
6

Existe um limite máximo de quanto tempo uma lista de argumentos pode ser para os comandos do sistema - este limite é específico da distribuição baseado no valor de MAX_ARG_PAGES quando o kernel é compilado e não pode ser alterado sem recompilar o kernel.

Devido à forma como o globbing é tratado pelo shell, isso afetará a maioria dos comandos do sistema quando você usar o mesmo argumento ("* .jpg"). Como o glob é processado pelo shell primeiro e depois enviado para o comando, o comando:

cp -uf *.jpg /targetdir/

é essencialmente o mesmo para o shell, como se você escrevesse:

cp -uf 1.jpg 2.jpg ... n-1.jpg n.jpg /targetdir/

Se você está lidando com muitos jpegs, isso pode se tornar incontrolável muito rápido. Dependendo da sua convenção de nomenclatura e do número de arquivos que você realmente precisa processar, é possível executar o comando cp em um subconjunto diferente do diretório por vez:

cp -uf /sourcedir/[a-m]*.jpg /targetdir/
cp -uf /sourcedir/[n-z]*.jpg /targetdir/

Isso pode funcionar, mas exatamente o quão eficiente seria com base em quão bem você pode dividir sua lista de arquivos em blocos convenientes que podem ser convertidos em blocos.

Globbable. Eu gosto dessa palavra.

Alguns comandos, como find e xargs , podem manipular listas de arquivos grandes sem fazer listas de argumentos com tamanho considerável.

find /sourcedir/ -name '*.jpg' -exec cp -uf {} /targetdir/ \;

O argumento -exec executará o restante da linha de comando uma vez para cada arquivo encontrado por find , substituindo o {} por cada nome de arquivo encontrado. Como o comando cp é executado apenas em um arquivo por vez, o limite da lista de argumentos não é um problema.

Isso pode ser lento devido ao processamento de cada arquivo individualmente. A utilização de xargs poderia fornecer uma solução mais eficiente:

find /sourcedir/ -name '*.jpg' -print0 | xargs -0 cp -uf -t /destdir/

xargs pode pegar a lista de arquivos completa fornecida por find e dividi-la em listas de argumentos de tamanhos gerenciáveis e executar cp em cada dessas sublistas.

Naturalmente, há também a possibilidade de apenas recompilar seu kernel, definindo um valor maior para MAX_ARG_PAGES . Mas recompilar um kernel é mais trabalho do que estou disposto a explicar nesta resposta.

    
por 19.08.2009 / 15:59
3

Isso acontece porque sua expressão curinga ( *.jpg ) excede o limite de comprimento do argumento da linha de comando quando expandida (provavelmente porque você tem muitos arquivos .jpg em /home/ftpuser/public_html/ftparea ).

Existem várias maneiras de contornar essa limitação, como usar find ou xargs . Dê uma olhada no artigo para obter mais detalhes sobre como faça isso.

    
por 19.08.2009 / 15:59
2

Existe um número máximo de argumentos que podem ser especificados para um programa, o bash expande * .jpg para muitos argumentos para o cp. Você pode resolvê-lo usando find, xargs ou rsync etc.

Dê uma olhada aqui sobre xargs e encontre

link

    
por 19.08.2009 / 15:57
2

Como GoldPseudo comentou, há um limite para quantos argumentos você pode passar para um processo que está gerando. Veja a resposta dele para uma boa descrição desse parâmetro.

Você pode evitar o problema ignorando o processo ou reduzindo o número de argumentos que está passando.

Um loop for no shell, find e ls, grep e um loop while fazem a mesma coisa nessa situação -

for file in /path/to/directory/*.jpg ; 
do
  rm "$file"
done

e

find /path/to/directory/ -name '*.jpg' -exec rm  {} \;

e

ls /path/to/directory/ | 
  grep "\.jpg$" | 
  while
    read file
  do
    rm "$file"
  done

todos têm um programa que lê o diretório (o próprio shell, o find e o ls) e um programa diferente que realmente recebe um argumento por execução e percorre toda a lista de comandos.

Agora, isso será lento porque o rm precisa ser bifurcado e executado para cada arquivo que corresponda ao padrão * .jpg.

É aqui que o xargs entra em jogo. O xargs toma a entrada padrão e para cada N (para o freebsd é por padrão 5000) linhas, ele gera um programa com N argumentos. O xargs é uma otimização dos loops acima, porque você só precisa separar os programas 1 / N para percorrer todo o conjunto de arquivos que lê os argumentos da linha de comando.

    
por 19.08.2009 / 18:51
1

Usar a opção + para find -exec acelerará bastante a operação.

find  /home/ftpuser/public_html/ftparea/ -name "*jpg" -exec cp -uf -t /your/destination "{}" +

A opção + requer que {} seja o último argumento, portanto, usar a opção -t /your/destination (ou --target-directory=/your/destination ) para cp faz com que funcione.

De man find :

-exec command {} +

          This  variant  of the -exec action runs the specified command on  
          the selected files, but the command line is built  by  appending  
          each  selected file name at the end; the total number of invoca‐  
          tions of the command will  be  much  less  than  the  number  of  
          matched  files.   The command line is built in much the same way  
          that xargs builds its command lines.  Only one instance of  ‘{}’  
          is  allowed  within the command.  The command is executed in the  
          starting directory.

Editar : argumentos rearranjados para cp

    
por 19.08.2009 / 16:12
1

Parece que você tem muitos arquivos *.jpg nesse diretório para colocá-los todos na linha de comando de uma só vez. Você poderia tentar:

find /home/ftpuser/public_html/ftparea1 -name '*.jpg' | xargs -I {} cp -uf {} /home/ftpuser/public_html/ftparea2/

Você pode precisar verificar man xargs para sua implementação para ver se a opção -I está correta para o seu sistema.

Na verdade, você está realmente pretendendo copiar esses arquivos para o mesmo local onde eles já estão?

    
por 19.08.2009 / 15:58
1

O '*' glob está expandindo para muitos nomes de arquivos. Use find / home / ftpuser / public_html -name '* .jpg' em seu lugar.

    
por 19.08.2009 / 15:57
0

Ir para a pasta

cd /home/ftpuser1/public_html/

e execute o seguinte:

cp -R ftparea/ /home/ftpuser2/public_html/

Desta forma, se a pasta 'ftparea' tiver subpastas, isso pode ser um efeito negativo se você quiser apenas os arquivos '* .jpg', mas se não houver subpastas, essa abordagem será definitivamente muito mais rápido do que usando find e xargs

    
por 09.04.2014 / 11:09