¿Cómo puedo dividir un comando de shell en varias líneas cuando uso una instrucción IF?

386

¿Cómo puedo dividir un comando en varias líneas en el shell, cuando el comando es parte de una ifdeclaración?

Esto funciona:

if ! fab --fabfile=.deploy/fabfile.py --forward-agent --disable-known-hosts deploy:$target; then rc=1                                                                       
fi

Esto no funciona

# does not work:
if ! fab --fabfile=.deploy/fabfile.py \ 
  --forward-agent \
  --disable-known-hosts deploy:$target; then   
  rc=1
fi

En lugar de ejecutar todo el comando, obtengo:

./script.sh: line 73: --forward-agent: command not found

Más importante aún, ¿qué falta en mi comprensión de Bash que me ayudará a comprender este y otros problemas similares en el futuro?

Dmitry Minkovsky
fuente
2
Cual es el error? Puedo ejecutar $ if ! cp -n log/server1.log \ > .; then echo no copy; fisin error, con una nueva línea después de\
Miserable Variable
15
¿Tiene espacios después de las barras diagonales inversas \ ? Son bastante difíciles de ver. Si lo hace, es posible que desee ver si puede hacer que su editor elimine los espacios finales o los haga más visibles.
msw
10
Sí, eran espacios después de las barras diagonales inversas. Totalmente. Gracias.
Dmitry Minkovsky
Y sí, lo siento, ¡debería haber publicado el "error" (resultado inesperado)! ¡Culpa mía! Editando ahora.
Dmitry Minkovsky
¿Qué entendiste? No es parte de la pregunta tampoco.
Hakre

Respuestas:

568

La continuación de línea fallará si tiene espacios en blanco (espacios o caracteres de tabulación) después de la barra diagonal inversa y antes de la nueva línea. Sin ese espacio en blanco, su ejemplo funciona bien para mí:

$ cat test.sh
if ! fab --fabfile=.deploy/fabfile.py \
   --forward-agent \
   --disable-known-hosts deploy:$target; then
     echo failed
else
     echo succeeded
fi

$ alias fab=true; . ./test.sh
succeeded
$ alias fab=false; . ./test.sh
failed

Algunos detalles promovidos de los comentarios: la barra invertida de continuación de línea en el shell no es realmente un caso especial; es simplemente una instancia de la regla general de que una barra invertida "cita" el carácter que sigue inmediatamente, evitando cualquier tratamiento especial al que normalmente estaría sujeto. En este caso, el siguiente carácter es una nueva línea, y el tratamiento especial que se evita es terminar el comando. Normalmente, un personaje citado termina incluido literalmente en el comando; una nueva línea con barra invertida se elimina por completo. Pero por lo demás, el mecanismo es el mismo. Y la barra diagonal inversa solo cita el carácter que sigue inmediatamente; si ese carácter es un espacio o una pestaña, solo obtienes un espacio o una pestaña entre comillas, y cualquier nueva línea subsiguiente permanece sin comillas.

Mark Reed
fuente
55
Mark, sabes, debo haber tenido espacios en blanco. Puedo reproducir el error solo cuando agrego espacios en blanco después del `s. For example, when adding one after the first `, obtengo ./soundops: line 73: --forward-agent: command not found. Mi problema fue que no entendí este error. ¿Por qué tener un espacio en blanco produce ese error? El espacio en blanco + \n"niega" el `` y delimita un comando?
Dmitry Minkovsky
84
Una barra invertida frente a la nueva línea evita que la nueva línea finalice el comando. Pero así como las secuencias de escape especiales como "\ n" solo funcionan sin nada entre la barra diagonal inversa y la n, la barra diagonal inversa-nueva línea solo funciona sin nada entre la barra diagonal inversa y la nueva línea.
Mark Reed
19
Jajaja wow, por supuesto que tiene sentido. Nunca lo vi así. Sorprendente, pero tan simple: es solo una nueva línea escapada. Odio los personajes invisibles. Tendrían mucho más sentido para mí si solo fueran visibles. ¡Gracias!
Dmitry Minkovsky
77
En la mayoría de los editores puedes hacer visibles esos caracteres invisibles.
lucasvc
1
La barra diagonal inversa y la nueva línea se eliminan de la línea de comando efectiva, pero se conserva cualquier espacio en blanco en la siguiente línea. Entonces, si es un problema o no, depende de si el espacio en blanco sería un problema en ese punto en el comando de una sola línea.
Mark Reed
52

Para usuarios de Windows / WSL / Cygwin, etc.

Asegúrese de que sus terminaciones de línea sean alimentaciones de línea estándar de Unix, es decir \n(LF) solamente.

El uso de los finales de línea de Windows \r\n(CRLF) terminará el salto de línea de comando.


Esto se debe a que tener \al final de una línea con final de línea de Windows se traduce en \ \r \n.
Como Mark explica correctamente anteriormente:

La continuación de línea fallará si tiene espacios en blanco después de la barra diagonal inversa y antes de la nueva línea.

Esto incluye no solo el espacio ( ) o las pestañas ( \t) sino también el retorno de carro ( \r).

Tecnología
fuente
1
Esto soluciona el problema creado al crear un script en Windows y luego usarlo en Windows bash (por ejemplo, bash -c MyShellScript.sh donde se creó MyShellScript.sh en el editor de Windows). Tienes que guardar MyShellScript.sh en formato UNIX, quizás usando notepad ++.
BSalita