Primero para cortar las respuestas triviales pero inaplicables: no puedo usar ni el truco find
+ xargs
ni sus variantes (como find
con -exec
) porque necesito usar pocas expresiones de este tipo por llamada. Volveré a esto al final.
Ahora, para un mejor ejemplo, consideremos:
$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
¿Cómo paso esos argumentos a program
?
Solo hacerlo no hace el truco
$ ./program $(find -L some/dir -name \*.abc | sort)
falla ya que program
obtiene los siguientes argumentos:
[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc
Como se puede ver, la ruta con el espacio se dividió y program
considera que son dos argumentos diferentes.
Cotizar hasta que funcione
Parece que los usuarios novatos como yo, cuando se enfrentan a tales problemas, tienden a agregar citas al azar hasta que finalmente funciona, solo que aquí no parece ayudar ...
"$(…)"
$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc
Debido a que las comillas evitan la división de palabras, todos los archivos se pasan como un solo argumento.
Citando caminos individuales
Un enfoque prometedor:
$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"
Las citas están ahí, claro. Pero ya no se interpretan. Son solo parte de las cuerdas. ¡Entonces no solo no impidieron la división de palabras, sino que también entraron en discusiones!
Cambiar IFS
Entonces traté de jugar con ellos IFS
. Preferiría find
con -print0
y sort
con de -z
todos modos, para que no tengan problemas en las "rutas cableadas". Entonces, ¿por qué no forzar la división de palabras en el null
personaje y tenerlo todo?
$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc
Por lo tanto, todavía se divide en el espacio y no se divide en el null
.
Traté de colocar la IFS
tarea tanto en $(…)
(como se muestra arriba) como antes ./program
. También probé otra sintaxis como \0
, \x0
, \x00
tanto citado con '
y "
así como con y sin el $
. Ninguno de esos parecía hacer ninguna diferencia ...
Y aquí estoy sin ideas. Intenté algunas cosas más, pero todas parecían tener los mismos problemas enumerados.
¿Qué más puedo hacer? ¿Es factible en absoluto?
Claro, podría hacer que program
acepte los patrones y hacer búsquedas por sí mismo. Pero es mucho trabajo doble mientras lo arregla a una sintaxis específica. (¿Qué hay de proporcionar archivos por un grep
por ejemplo?).
También podría hacer que program
acepte un archivo con una lista de rutas. Entonces puedo volcar fácilmente la find
expresión en algún archivo temporal y proporcionar la ruta solo a ese archivo. Esto podría admitirse a lo largo de rutas directas, de modo que si el usuario solo tiene una ruta simple, se puede proporcionar sin archivo intermedio. Pero esto no parece agradable: uno necesita crear archivos adicionales y cuidarlos, sin mencionar la implementación adicional requerida. (En el lado positivo, sin embargo, podría ser un rescate para los casos en que la cantidad de archivos como argumentos comienzan a causar problemas con la longitud de la línea de comandos ...)
Al final, déjame recordarte nuevamente que los trucos find
+ xargs
(y similares) no funcionarán en mi caso. Para simplificar la descripción, solo estoy mostrando un argumento. Pero mi verdadero caso se parece más a esto:
$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES
Así que hacer una xargs
búsqueda de uno todavía me deja con la forma de lidiar con el otro ...
fuente
mapfile
(o su sinónimoreadarray
). ¡Pero funciona!while
bucle no borra la matriz. Lo que significa que si no se encuentran archivos, la matriz no está definida. Mientras que si ya está definido, se agregarán nuevos archivos (en lugar de reemplazar los antiguos). Parece que agregardeclare -a ABC_FILES='()';
anteswhile
hace el truco. (Aunque solo agregaABC_FILES='()';
no.)< <
significa aquí? ¿Es lo mismo que<<
? No creo que cambiarlo para<<
producir un error de sintaxis ("token inesperado` ('"). Entonces, ¿qué es y cómo funciona?ABC_FILES
. Eso está bien. Pero también es útil hacerABS_ARGS
cuál es una matriz vacía siABC_FILES
está vacía o si no es una matriz('--abc-files' "${ABC_FILES[@]}")
. De esta manera más adelante, puedo usarlo así:./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"
y asegurarme de que funcionará correctamente, independientemente de cuál (si alguno) de los grupos está vacío. O para decirlo de manera diferente: de esta manera--abc-files
(y--xyz-files
) se proporcionará solo si es seguido por alguna ruta real.while read ... done < <(find blah)
es la redirección de shell normal<
desde un archivo especial creado por SUSTITUCIÓN DE PROCESO . Esto difiere de la tuberíafind blah | while read ... done
porque la tubería ejecuta elwhile
bucle en una subshell, por lo que las var (s) establecidas en ella no se retienen para los comandos posteriores.Puede usar IFS = nueva línea (suponiendo que ningún nombre de archivo contenga nueva línea) pero debe establecerlo en la capa externa ANTES de la sustitución:
Con
zsh
pero nobash
puedes usar nulo$'\0'
también. Incluso enbash
usted podría manejar la nueva línea si hay un personaje suficientemente extraño que nunca se usa comoSin embargo, este enfoque no maneja la solicitud adicional que hizo en los comentarios sobre la respuesta de @ steeldriver para omitir los --afiles si find a está vacío.
fuente
IFS
divisiónnull
?read -d ''
en cuenta que el método utilizado en steeldriver es una cadena vacía que no contiene un byte nulo. (Y una opción de comando no es una var como tal de todos modos.)set -o noglob
) antes de usar ese operador split + glob (excepto enzsh
).$'\0'
y también como''
.No estoy seguro de entender por qué renunciaste
xargs
.La cadena
--xyz-files
es solo uno de los muchos argumentos y no hay razón para considerarla especial antes de que su programa la interprete. Creo que puedes pasarloxargs
entre ambosfind
resultados:fuente
-print0
en cuenta que se perdió en el segundofind
. Además, si voy de esta manera, también lo pondría--abc-files
comoecho
solo, solo por coherencia..abc
archivos, entonces tampoco debería haber--abc-files
(lo mismo con.xyz
). La solución basada en matriz de steeldriver también requiere lógica adicional para ella, pero esa lógica es trivial allí, mientras que podría no ser tan trivial aquí, destruyendo la principal ventaja de esta solución: la simplicidad.xargs
nunca va a tratar de dividir argumentos y hacer algunos comandos en lugar de uno, a menos que se explícitamente instruido para hacerlo con-L
,--max-lines
(-l
),--max-args
(-n
) o--max-chars
(-s
) argumentos. Estoy en lo cierto? ¿O hay algunos valores predeterminados? Como mi programa no manejaría tal división correctamente y preferiría no llamarlo ...-print0
- arreglado, gracias. No sé todas las respuestas, pero estoy de acuerdo con que mi solución hace que sea difícil incluir una lógica adicional. Probablemente iría con matrices, ahora que conozco este enfoque. Mi respuesta no fue realmente para ti. Ya había aceptado la otra respuesta y asumí que su problema está resuelto. Solo quería señalar que puede pasar argumentos de múltiples fuentesxargs
, lo que no era obvio a primera vista. Puede tratarlo como una prueba de concepto. Ahora todos conocemos algunos enfoques diferentes y podemos elegir conscientemente lo que nos conviene en cada caso particular.--abc-files
). Pero tiene razón: ¡es bueno conocer sus alternativas! Especialmente que erróneamente pensé que no es posible.