El comando Bash en cadena se ejecuta cuando creo la cadena, no cuando la uso más tarde

10

Soy relativamente nuevo en el script de shell, pero casi he completado un script que hace uso del programa lftp . La parte del script con la que tengo problemas es cuando creo una larga cadena de comandos (separados por ;).

for var in something
do
    ...
    commands_to_run+="echo Result is `tail -n 1 $somefile`;"
done

Lo que estoy notando es que el tailprograma, envuelto en los backticks, se está ejecutando cuando el ciclo for está iterando, pero no cuando invoco la cadena de comandos más adelante en mi script.

Desafortunadamente, el contenido de $ somefile no está en esta etapa listo para ser inspeccionado. ¿Cómo puedo ejecutar el comando cuando lo necesito, y no mientras estoy creando la cadena?

Ricky
fuente

Respuestas:

8

Este es un poco complicado. La información que Hauke ​​ha proporcionado es correcta, solo es cuestión de analizarla para su caso de uso.

La forma más fácil es usar la $()sintaxis mientras escapa de $tal manera que la definición de la variable no ejecute el comando encerrado $()en el momento de la definición. La advertencia es que el resultado final debe ser reevaluado (vía eval) por el shell en el momento de la ejecución real para que se ejecute el comando anidado.

Es mucho más fácil mirar un ejemplo, así que tome este, que debería ponerlo en el camino correcto:

for((i=0;i<10;i++)); do 
  date +%s.%N  # Print a timestamp (in format seconds.nanoseconds)
  test="echo \$(date +%s.%N)" # Save a command to do the same
  sleep 1      # Sleep for a second
  eval "$test" # Evaluate the command saved in variable 'test'
  echo         # Print a new line before the next iteration
done

Aquí está la salida de muestra del ejemplo anterior (recortado a una iteración):

1398832186.133661344
1398832187.139076728

Notarás que la segunda marca de tiempo para cada bucle es aproximadamente un segundo después de la primera. Por el contrario, si realiza la misma prueba sin escapar $de la testdefinición y quitando la eval, las dos marcas de tiempo casi coincidirán.

No tenga el hábito de usarlo evalen la mayoría de las situaciones, pero esta es una de esas en las que no conozco una buena manera de evitarlo. Espero que esto ayude. ¡Buena suerte!

daBeamer
fuente
Muchas gracias, intenté usar $(...)como sugirió Hauke, pero la barra diagonal inversa es la clave.
Ricky
Me alegro de que haya ayudado; recuerde, sin embargo, que la clave aquí realmente es evalque puede hacer lo mismo al no escapar de las $comillas simples ( ') en lugar de las comillas dobles ( ") para rodear su comando.
daBeamer
Ahora me acabo de dar cuenta, al igual que con las sugerencias de Huake, una vez que trato de usar esto en el programa lftp, el eco simplemente imprime el comando, en realidad no lo ejecutará. Puede que tenga que probar su lista de correo para obtener ayuda más específica.
Ricky
¿Qué comando estás tratando de ejecutar? Tenía la impresión de que quería echouna cadena con contenido que incluye la salida de un comando anidado de ejecución demorada.
daBeamer
1
@Ricky Tendría que estar de acuerdo con todos los puntos de @HaukeLaging. El código tal como está menos el echono va a funcionar porque no hay un comando para hacerlo eval, sino más bien una cadena. Si tiene un ejemplo más pertinente para nosotros, podemos intentar ayudarlo.
daBeamer
6

Hay varios niveles de citas. Las comillas dobles ( "...") espacios en blanco proteger y varios caracteres especiales ( ~, &, |, ;, ...) pero no impide la expansión de parámetros y la sustitución de comandos.

Necesita comillas simples ( ') o una barra diagonal inversa delante de los caracteres "peligrosos".

En general: debe considerar usar en $(tail ...)lugar de backticks. Los backticks son el estándar más antiguo, pero estamos hablando de tan antiguo que $()no causa problemas a la mayoría de las personas. La nueva versión es más fácil de leer y puede anidarse. Y mucho menos los problemas de formato aquí en sx ...

Hauke ​​Laging
fuente
Gracias por la rápida respuesta Hauke. Desafortunadamente, la sustitución de los backticks con los recomendados $(...)aún produce el mismo resultado: el shell ejecuta esto cuando se define mi cadena.
Ricky
@Ricky Eso no fueron sugerencias alternativas. Deberá usar $()pero necesita las comillas simples de todos modos.
Hauke ​​Laging
Entonces, ¿ninguna combinación de esos personajes logrará lo que busco?
Ricky
@Ricky ¿Qué es tan difícil de entender sobre "Necesita comillas simples"? Obviamente ni siquiera lo intentas.
Hauke ​​Laging
En realidad lo hice, pero cuando uso comillas simples, el eco simplemente imprimirá todo en esa línea como una cadena normal. ¿Qué es tan difícil de dar un ejemplo?
Ricky