¿Cómo 'find -exec' pasa nombres de archivos con espacios?

14

Si tengo un directorio que contiene algunos archivos cuyos nombres tienen espacios, por ejemplo

$ ls -1 dir1
file 1
file 2
file 3

Puedo copiarlos con éxito a otro directorio como este:

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

Sin embargo, la salida de find dir1 -mindepth 1contiene espacios sin escape:

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

Si uso en print0lugar de print, la salida todavía contiene espacios sin escape:

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

Para copiar estos archivos manualmente usando cp, necesitaría escapar de los espacios; pero parece que esto es innecesario cuando cpprovienen los agumentos find, independientemente de si uso +o \;al final del comando.

¿Cuál es la razón de esto?

EmmaV
fuente

Respuestas:

8

El findcomando ejecuta el comando directamente. El comando, incluido el argumento del nombre de archivo, no será procesado por el shell ni nada que pueda modificar el nombre de archivo. Es muy seguro

Tiene razón en que no hay necesidad de escapar de los nombres de archivo que se representan {}en la findlínea de comando.

findpasa el nombre de archivo sin procesar del disco directamente a la lista de argumentos internos del -execcomando, en su caso, el cpcomando.

RobertL
fuente
1
En pocas palabras, find..execpuede manejar nombres de archivos extraños en su propia ..
heemayl
2
La primera regla del linux club es que no analices ls
Sergiy Kolodyazhnyy
5

La pregunta es de dos partes:

  • ¿ Cómo se las find arregla para llamar a los programas usando -execsin tener problemas con los espacios incrustados en los nombres de archivo, y
  • ¿de qué sirve la -print0opción?

Para el primero, findestá haciendo una llamada al sistema, en realidad una de un grupo de llamadas relacionadas a las que se hace referencia como "exec" . Pasa el nombre de archivo como argumento directamente a esta llamada, que luego se pasa directamente (después de crear un nuevo proceso) sin perder información sobre el nombre de archivo.

La findcaracterística POSIX +se explica de la siguiente manera, en la justificación :

Una característica de la findutilidad de SVR4 era el -execterminador + primario. Esto permitió que los nombres de archivo que contenían caracteres especiales (especialmente los caracteres de nueva línea ) se agruparan sin los problemas que se producen si se canalizan dichos nombres de archivo xargs. Otras implementaciones han agregado otras formas de solucionar este problema, especialmente una -print0primaria que escribió nombres de archivo con un terminador de bytes nulo. Esto se consideró aquí, pero no se adoptó. El uso de un terminador nulo significaba que cualquier utilidad que procesara la -print0salida de find tenía que agregar una nueva opción para analizar los terminadores nulos que ahora estaría leyendo.

Que " notablemente un -print0primario" se refiere a GNU findy xargsque resuelve el problema de una manera diferente. También es compatible con FreeBSD findy xargs. Si agregó una -0opción (consulte la página del manual ) a la xargsllamada, ese programa acepta líneas terminadas con caracteres de "byte nulo". A su vez, xargsllama a exec -functions para que haga su trabajo. La distinción principal entre el -print0y -0característica frente a la +característica es que el primero pasa a los nombres de archivo más de una tubería, mientras que el segundo no. Los desarrolladores encuentran usos para casi cualquier característica; Las tuberías no son la excepción.

Volviendo al ejemplo de OP, que utiliza una -topción para cp: eso no se encuentra en POSIX cp . Más bien, es una extensión (también conocida como "característica no estándar") proporcionada por GNU cp . La -0extensión de xargsno mejoraría este ejemplo, pero hay otros casos en los que se puede usar de manera efectiva, teniendo en cuenta que existe una alternativa portátil +, que GNU findacepta.

Thomas Dickey
fuente
-1

( Esto debería ser un comentario pero es demasiado grande ) .

Para aquellos que les gusta probar cosas:

Cree un script que enumere los parámetros posicionales pasados, llámelo list_positional_parameters.sh.

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

Ejecútelo finden algún directorio $ dir:

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

Como se esperaba, solo hay un único parámetro en todas las llamadas, el nombre de archivo, si hay espacios en su nombre o no.

David Tonhofer
fuente
1
También puede usar printflike printf '"%s"\n' "$@"para imprimir todos los argumentos posicionales citados, para inspección visual.
Kusalananda