
En resumen, quiero usar directorios enumerados por un comando en un find
comando:
find $(produces_dir_names --options...) -find-options...
El problema surge con los espacios en blanco en los nombres de los directorios. Pensé que citarlos en el resultado del comando de producción (que puedo cambiar) sería suficiente:
"a" "a b" "a b c"
pero bash se queja:
find: ‘"a"’: No such file or directory
find: ‘"a’: No such file or directory
find: ‘b"’: No such file or directory
find: ‘"a’: No such file or directory
find: ‘b’: No such file or directory
find: ‘c"’: No such file or directory
Como puede, ver bash
dividirá la salida del comando en espacios, incluso con las comillas. Intenté manipularlo IFS
y configurarlo \n
, pero mi comprensión parece demasiado limitada para que funcione.
La única solución que encontré fue en esta pregunta de Stack Overflow:
sustitución del comando bash eliminar cita, es decir, poner un eval
delante, pero esto parece un poco feo.
Mis preguntas:
¿Existe una manera fácil y cómo se vería escribir esta sustitución, sin eval
?
¿Son necesarias las citas?
Ejemplo (produciendo el mismo resultado):
find $(echo '"a" "a b" "a b c"')
Respuesta1
Tal vez en dos líneas
IFS=$'\n' DIRS=( $(produces_dir_names --options...) )
find "${DIRS[@]}" -find-options...
Ejemplo:
$ mkdir -p "/tmp/test/a b/foo" "/tmp/test/x y/bar"
$ IFS=$'\n' DIRS=( $(printf "/tmp/test/a b\n/tmp/test/x y\n") )
$ find "${DIRS[@]}" -mindepth 1
/tmp/test/a b/foo
/tmp/test/x y/bar
Pero en general éste no es un buen estilo. Por ejemplo, tendrá problemas si su DIRS contiene nuevas líneas. Mejor arregle sus "produces_dir_names" para imprimir cadenas terminadas en bytes nulos. Respecto a mi ejemplo, esto sería algo como:
$ printf "/tmp/test/a b\0/tmp/test/x y\0" | xargs -0 -I '{}' find '{}' -mindepth 1
/tmp/test/a b/foo
/tmp/test/x y/bar
Si no puedes corregir "produces_dir_names", con respecto a mi último comentario, la solución más general sería la siguiente:
produces_dir_names --options... | tr '\n' '\0' | xargs -0 -I '{}' find '{}' -find-options...
Aún hay problemas con las "nuevas líneas" a menos que arregles "produces_dir_names" para evitarlos tr
.
Respuesta2
la respuesta de rudimeieres bueno, específicamente, la parte sobre la modificación produces_dir_names
para imprimir cadenas terminadas en nulo, pero puede que no sea obvio por su respuesta que se ejecuta find
una vez para cada directorio. Si esto es lo suficientemente bueno, entonces está bien. Pero, por supuesto, es posible invocar find
con múltiples puntos de partida; p.ej,
encontrar dirección 1 dirección 2 dirección 3 -opciones de búsqueda...
y de la pregunta se desprende que eso es lo que quieres. Esto puede hacerse de la siguiente manera:
printf "a\0a b\0a b c" | xargs -0 sh -c 'buscar "$@" -opciones de búsqueda...' sh
Esto hace xargs
que se invoque sh -c
una vez, con todos los nombres de directorio agregados al comando. Luego, el shell se expandirá "$@"
a una lista de esos nombres de directorio.
PD: Si produces_dir_names
enumera demasiados nombres de directorios para ponerlos en una línea de comando, xargs
se verá obligado a generar algunos comandos. Úselo xargs --verbose
para ver qué comandos xargs
se generan.
Respuesta3
Sólo para aclarar el misterio de los mensajes de error que recibe:
find: ‘"a"’: No such file or directory find: ‘"a’: No such file or directory find: ‘b"’: No such file or directory find: ‘"a’: No such file or directory find: ‘b’: No such file or directory find: ‘c"’: No such file or directory
La respuesta es queLa eliminación de comillas de Bash no elimina las comillas queresultadode la sustitución de mando.
DeLESS='+/^ *Quote Removal' man bash
Quote Removal After the preceding expansions, all unquoted occurrences of the charac- ters \, ', and " that did not result from one of the above expansions are removed.
Las "expansiones anteriores" a las que se hace referencia incluyen:
EXPANSION Brace Expansion Tilde Expansion Parameter Expansion Command Substitution Arithmetic Expansion Process Substitution Word Splitting Pathname Expansion Quote Removal