Siguiendo desde: comportamiento inesperado en la sustitución de comandos de shell
Tengo un comando que puede tomar una gran lista de argumentos, algunos de los cuales pueden contener espacios legítimamente (y probablemente otras cosas)
Escribí un script que puede generar esos argumentos para mí, con comillas, pero debo copiar y pegar la salida, por ejemplo
./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>
Traté de simplificar esto simplemente haciendo
./othercommand $(./somecommand)
y se topó con el comportamiento inesperado mencionado en la pregunta anterior. La pregunta es: ¿se puede usar la sustitución de comandos de manera confiable para generar los argumentos othercommanddado que algunos argumentos requieren comillas y esto no se puede cambiar?
shell
arguments
command-substitution
usuario1207217
fuente
fuente

evalpodría usarse, pero generalmente no se recomienda.xargses algo a considerar tambiénsomecommandsometa a un análisis de shell regular:) ... suponiendo que ese carácter no esté confiablemente en la salida.Respuestas:
Si la salida se cita correctamente para el shell y confía en la salida , entonces podría ejecutarla
eval.Suponiendo que tiene un shell que admite matrices, sería mejor usar uno para almacenar los argumentos que obtiene.
Si
./gen_args.shproduce resultados como'foo bar' '*' asdf, entonces podríamos ejecutareval "args=( $(./gen_args.sh) )"para completar una matriz llamadaargscon los resultados. Eso sería los tres elementosfoo bar,*,asdf.Podemos usar
"${args[@]}"como de costumbre para expandir los elementos de la matriz individualmente:(Tenga en cuenta las comillas. Se
"${array[@]}"expande a todos los elementos como argumentos distintos sin modificar. Sin comillas, los elementos de la matriz están sujetos a la división de palabras. Consulte, por ejemplo, la página Arrays en BashGuide ).Sin embargo ,
evalejecutará$HOMEcon gusto cualquier sustitución de shell, por lo que en la salida se expandiría a su directorio de inicio, y una sustitución de comando realmente ejecutaría un comando en la ejecución de shelleval. Una salida de"$(date >&2)"crearía un único elemento de matriz vacío e imprimiría la fecha actual en stdout. Esto es preocupante sigen_args.shobtiene los datos de alguna fuente no confiable, como otro host en la red, nombres de archivos creados por otros usuarios. La salida podría incluir comandos arbitrarios. (Si enget_args.shsí mismo fuera malicioso, no necesitaría generar nada, simplemente podría ejecutar los comandos maliciosos directamente).Una alternativa a la cita de shell, que es difícil de analizar sin eval, sería usar algún otro carácter como separador en la salida de su script. Debería elegir uno que no sea necesario en los argumentos reales.
Vamos a elegir
#y tener la salida del scriptfoo bar#*#asdf. Ahora podemos usar la expansión de comandos sin comillas para dividir la salida del comando en los argumentos.Tendrá que volver a configurarlo
IFSmás tarde si depende de la división de palabras en otra parte del script (unset IFSdebería funcionar para que sea el predeterminado), y también usarloset +fsi desea usar globbing más adelante.Si no está utilizando Bash o algún otro shell que tenga matrices, puede usar los parámetros posicionales para eso. Reemplace
args=( $(...) )conset -- $(./gen_args.sh)y use en"$@"lugar de"${args[@]}"entonces. (Aquí, también, necesita comillas"$@", de lo contrario los parámetros posicionales están sujetos a la división de palabras).fuente
${args[@]}"${array[@]}"con"$@". Ambos necesitan ser citados, o la división de palabras divide los elementos de la matriz en partes.El problema es que una vez que su
somecommandscript genera las opcionesothercommand, las opciones son realmente solo texto y a merced del análisis estándar del shell (afectado por lo$IFSque sea que sea y qué opciones de shell están vigentes, lo que en el caso general no tener el control sobre).En lugar de usar
somecommandpara generar las opciones, sería más fácil, más seguro y más robusto usarlo para llamarothercommand. Lasomecommandsecuencia de comandos sería una secuencia de comandos envolvente enothercommandlugar de una especie de secuencia de comandos auxiliar a la que tendría que recordar llamar de alguna manera especial como parte de la línea de comandosotherscript. Los scripts de envoltura son una forma muy común de proporcionar una herramienta que simplemente llama a otra herramienta similar con otro conjunto de opciones (solo verifiquefilequé comandos en/usr/binrealidad son envoltorios de secuencia de comandos de shell).En
bash,kshozsh, podría fácilmente una secuencia de comandos de contenedor que utiliza una matriz para contener las opciones individuales de la siguienteothercommandmanera:Luego llame
othercommand(aún dentro de la secuencia de comandos del contenedor)La expansión de
"${options[@]}"garantizaría que cada elemento de laoptionsmatriz se cite individualmente y se presenteothercommandcomo argumentos separados.El usuario de la envoltura sería ajeno al hecho de que en realidad está llamando
othercommand, algo que no sería cierto si el script solo generara las opciones de línea de comandoothercommandcomo salida.En
/bin/sh, use$@para mantener las opciones:(
setEs el comando que se utiliza para ajustar los parámetros posicionales$1,$2,$3etc. Estos son lo que constituye la matriz$@en una cáscara de POSIX estándar. La primera--es para indicar asetque no existen opciones dadas, sólo argumentos. El--está realmente sólo es necesario si el el primer valor resulta ser algo que comienza con-).Tenga en cuenta que son las comillas dobles
$@y${options[@]}eso asegura que los elementos no se dividan individualmente en palabras (y que el nombre de archivo sea global).fuente
set --?Si la
somecommandsalida tiene una sintaxis de shell confiablemente buena, puede usareval:Pero debe asegurarse de que la salida tenga citas válidas y demás, de lo contrario podría terminar ejecutando comandos también fuera del script:
Tenga en cuenta que
echo rm bar baz test.shno se pasó al script (debido a;) y se ejecutó como un comando separado. Agregué el|alrededor$varpara resaltar esto.En general, a menos que pueda confiar completamente en la salida de
somecommand, no es posible usar de manera confiable su salida para construir una cadena de comando.fuente