Tengo un script bash que construye una línea de comandos en una cadena basada en algunos parámetros antes de ejecutarlo de una vez. Se supone que las partes que se concatenan en la cadena de comando están separadas por tuberías para facilitar una "transmisión" de datos a través de cada componente.
Un ejemplo muy simplificado:
#!/bin/bash
part1=gzip -c
part2=some_other_command
cmd="cat infile"
if [ ! "$part1" = "" ]
then
cmd+=" | $part1"
fi
if [ ! "$part2" = "" ]
then
cmd+=" | $part2"
fi
cmd+="> outfile"
#show command. It looks ok
echo $cmd
#run the command. fails with pipes
$cmd
Por alguna razón, las tuberías no parecen funcionar. Cuando ejecuto este script, recibo diferentes mensajes de error relacionados generalmente con la primera parte del comando (antes de la primera canalización).
Entonces, mi pregunta es si es posible o no construir un comando de esta manera, y ¿cuál es la mejor manera de hacerlo?
infile
existe en el directorio actual?Respuestas:
Todo depende de cuándo se evalúan las cosas. Cuando escribe
$cmd
, todo el resto de la línea se pasa como argumentos a la primera palabra$cmd
.Esto muestra que los argumentos pasados al
echo
comando son: "/etc/passwd
", "|
" (el carácter de la barra vertical), "wc
" y "-l
".De
man bash
:fuente
Una solución a esto, para referencia futura, es usar "eval". Esto garantiza que cualquier manera en que bash interprete la cadena se olvide y que todo se lea como si estuviera escrito directamente en un shell (que es exactamente lo que queremos).
Entonces, en el ejemplo anterior, reemplazando
con
resuelto.
fuente
eval foo "a b"
sería lo mismo queeval foo "a" "b"
.@waltinator ya explicó por qué esto no funciona como esperaba. Otra forma de evitarlo es usar
bash -c
para ejecutar su comando:fuente
bash -c
, sino que useeval
para hacer el comando en el proceso actual.Posiblemente, una mejor manera de hacer esto es evitar usar
eval
y simplemente usar una matriz Bash y es una expansión en línea para construir todos los argumentos y luego ejecutarlos contra el comando.fuente