Error de "Lista de argumentos demasiado larga" al copiar una gran cantidad de archivos

12

Estoy usando el siguiente comando:

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

Y recibo el error:

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

También he intentado:

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

Todavía tengo -bash: / bin / ls: la lista de argumentos es demasiado larga

¿Algunas ideas?

icelizard
fuente
Estoy tratando de copiar todos los archivos jpgs de 1 directorio a otro, pero solo los archivos nuevos y los que se han actualizado.
icelizard
lsno está diseñado para hacer este tipo de cosas. Uso find.
Pausado hasta nuevo aviso.
El problema no es con ls, es con la cantidad de argumentos que el shell pasa a ls. Obtendría el mismo error con vi o con cualquier comando no incorporado.
chris
Pero lses sobre todo que no está diseñado para hacer esto: mywiki.wooledge.org/ParsingLs
En pausa hasta nuevo aviso.
Es cierto, pero en este caso el error no se debe a un error de análisis con ls, sino al paso de mil millones de argumentos a un nuevo proceso que resulta ser ls. Además de ser un uso inapropiado de ls, también se topa con una limitación de recursos / diseño de unix. En este caso, el paciente tiene dolor de estómago y una pierna rota.
Chris

Respuestas:

19

* .jpg se expande a una lista más larga de lo que el shell puede manejar. Intenta esto en su lugar

find  /home/ftpuser/public_html/ftparea/ -name "*.jpg" -exec cp -uf "{}" /your/destination \;
Shawn Chin
fuente
Utilicé find / home / ftpuser1 / public_html / ftparea / -name "* jpg" -exec cp -uf "{}" / home / ftpuser2 / public_html / ftparea / y obtuve el siguiente error de búsqueda: falta un argumento para `-exec '
icelizard
Te estás perdiendo el último argumento de cp, el contestador te dijo bien. Verifique su implementación. Tenga en cuenta que en esta respuesta falta el punto en "* .jpg", esto podría conducir a un mal comportamiento (por ejemplo, cp un directorio llamado "myjpg"). Nota entonces que puede ser paranoico, pero más seguro especificar de cerca lo que se va a copiar utilizando el archivo de tipo (prevención de directorios, enlaces simbólicos, etc. que serán afectadas)
drAlberT
Después de una inspección más cercana, me perdí el "\;" para finalizar el comando que -exec debería ejecutar. ¡Tonto de mí!
icelizard
@AlberT: gracias por las cabezas del punto faltante. Eso fue un error tipográfico. Respuesta actualizada
Shawn Chin
No es que cp no pueda manejarlo. El caparazón no puede.
d -_- b
6

Existe un límite máximo para la duración de una lista de argumentos para los comandos del sistema: este límite es específico de la distribución según el valor de MAX_ARG_PAGEScuándo se compila el núcleo, y no se puede cambiar sin volver a compilar el núcleo.

Debido a la forma en que el shell maneja el globbing, esto afectará a la mayoría de los comandos del sistema cuando use el mismo argumento ("* .jpg"). Dado que el shell primero procesa el glob y luego lo envía al comando, el comando:

cp -uf *.jpg /targetdir/

es esencialmente lo mismo para el shell como si escribieras:

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

Si está lidiando con muchos archivos JPEG, esto puede volverse inmanejable muy rápido. Dependiendo de su convención de nomenclatura y la cantidad de archivos que realmente tiene que procesar, puede ejecutar el comando cp en un subconjunto diferente del directorio a la vez:

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

Esto podría funcionar, pero exactamente qué tan efectivo sería se basa en qué tan bien puede dividir su lista de archivos en convenientes bloques globbables.

Globbable Me gusta esa palabra

Algunos comandos, como find y xargs , pueden manejar grandes listas de archivos sin hacer listas de argumentos de gran tamaño.

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

El argumento -exec ejecutará el resto de la línea de comando una vez para cada archivo encontrado por find , reemplazando el {} con cada nombre de archivo encontrado. Dado que el comando cp solo se ejecuta en un archivo a la vez, el límite de la lista de argumentos no es un problema.

Esto puede ser lento debido a que tiene que procesar cada archivo individualmente. Usar xargs podría proporcionar una solución más eficiente:

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

xargs puede tomar la lista completa de archivos provista por find y dividirla en listas de argumentos de tamaños manejables y ejecutar cp en cada una de esas sublistas.

Por supuesto, también existe la posibilidad de recompilar su kernel, estableciendo un valor mayor para MAX_ARG_PAGES. Pero recompilar un kernel es más trabajo del que estoy dispuesto a explicar en esta respuesta.

goldPseudo
fuente
No tengo idea de por qué esto fue rechazado. Es la única respuesta que parece explicar por qué sucede esto. ¿Quizás porque no sugirió usar xargs como optimización?
chris
agregó en la solución xargs, pero todavía estoy preocupado de que los votos negativos se deban a algo descaradamente incorrecto en mis detalles y nadie quiere decirme qué es. :(
goldPseudo
xargsparece ser mucho más eficiente, ya que el número resultante de llamadas de comando es mucho menor. En mi caso, veo un rendimiento entre 6 y 12 veces mejor cuando lo uso, argsy cuando uso una -execsolución con un número creciente de archivos aumenta la eficiencia.
Jan Vlcinsky
3

Eso sucede porque su expresión comodín ( *.jpg) excede el límite de longitud del argumento de la línea de comando cuando se expande (probablemente porque tiene muchos archivos .jpg debajo /home/ftpuser/public_html/ftparea).

Hay varias formas de sortear esa limitación, como usar findo xargs. Eche un vistazo a este artículo para obtener más detalles sobre cómo hacerlo.

mfriedman
fuente
+1 por el buen recurso externo sobre el tema.
viam0Zah
3

Como comentó GoldPseudo, hay un límite en la cantidad de argumentos que puede pasar a un proceso que está generando. Vea su respuesta para una buena descripción de ese parámetro.

Puede evitar el problema al no pasar demasiados argumentos al proceso o al reducir el número de argumentos que pasa.

Un bucle for en el shell, find, y ls, grep y un bucle while hacen lo mismo en esta situación:

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

y

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

y

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

todos tienen un programa que lee el directorio (el propio shell, find y ls) y un programa diferente que realmente toma un argumento por ejecución e itera a través de la lista completa de comandos.

Ahora, esto será lento porque la rm debe bifurcarse y ejecutarse para cada archivo que coincida con el patrón * .jpg.

Aquí es donde entra en juego xargs. xargs toma la entrada estándar y por cada N (para freebsd es por defecto 5000) líneas, genera un programa con N argumentos. xargs es una optimización de los bucles anteriores porque solo necesita bifurcar programas 1 / N para iterar sobre todo el conjunto de archivos que leen argumentos desde la línea de comandos.

Chris
fuente
1

El globo '*' se está expandiendo a demasiados nombres de archivo. Use find / home / ftpuser / public_html -name '* .jpg' en su lugar.

William Pursell
fuente
Find y echo * dan como resultado la misma salida: la clave aquí es usar xargs no solo pasar todos los mil millones de argumentos de línea de comando al comando que el shell intenta bifurcar.
chris
echo * fallará si hay demasiados archivos, pero find tendrá éxito. Además, usar find -exec con + es equivalente a usar xargs. (No todos encuentran soporte +, sin embargo)
William Pursell
1

Usar la +opción para find -execacelerará enormemente la operación.

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

La +opción requiere {}ser el último argumento, por lo que usar la opción -t /your/destination(o --target-directory=/your/destination) para cpque funcione.

De man find:

-exec comando {} +

          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 reorganizados para cp

Pausado hasta nuevo aviso.
fuente
Obtengo find: falta un argumento para `-exec '/ home / ftpuser1 / public_html / ftparea / -name' * jpg '-exec cp -uf" {} "/ home / ftpuser2 / public_html / ftparea / +
icelizard
Reorganicé los argumentos cppara corregir ese error.
Pausado hasta nuevo aviso.
1

Parece que tiene demasiados *.jpgarchivos en ese directorio para ponerlos a todos en la línea de comando a la vez. Tu podrías intentar:

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

Es posible que deba verificar man xargssu implementación para ver si el -Iconmutador es correcto para su sistema.

En realidad, ¿realmente tiene la intención de copiar esos archivos en la misma ubicación donde ya están?

Greg Hewgill
fuente
disculpas se trata de dos directorios diferentes deben ser ftpuser1 y ftpuser2
icelizard
Acabo de probar esto: ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} / home / ftpuser2 / public_html / ftparea / Still got -bash: / bin / ls: Lista de argumentos demasiado larga
icelizard
¡Oh, tienes toda la razón, por supuesto lstendrá el mismo problema! He cambiado a findlo que no.
Greg Hewgill
0

Ir a la carpeta

cd /home/ftpuser1/public_html/

y ejecuta lo siguiente:

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

De esta manera, si la carpeta 'ftparea' tiene subcarpetas, esto podría ser un efecto negativo si solo desea los archivos '* .jpg', pero si no hay ninguna subcarpeta, este enfoque será definitivamente mucho más rápido que usando find y xargs

pinpinokio
fuente