bash - ¿Por qué `if $(verdadero); entonces... fi'' éxito?
ksh zsh (4)
¿Qué debe hacer una instrucción if cuando la condición es una sustitución de comando donde el comando no produce salida?
La salida no importa. Lo que importa es el código de salida :
if list; then list; [ elif list; then list; ] ... [ else
list; ] fi
The if list is executed. If its exit status is zero,
the then list is executed. Otherwise, each elif list
is executed in turn, and if its exit status is zero,
the corresponding then list is executed and the
command completes. Otherwise, the else list is
executed, if present. The exit status is the exit
status of the last command executed, or zero if no
condition tested true.
Si reemplaza su $(echo true)
y $(echo false)
con otra cosa, probablemente verá lo que está sucediendo:
$ if $(echo false) ; then echo yes ; else echo no ; fi
no
$ if $(echo command-does-not-exist) ; then echo yes ; else echo no ; fi
command-does-not-exist: command not found
no
$
El $(..)
ejecuta el comando y luego, if
ejecuta los resultados (en este caso, simplemente true
o false
o does-not-exist
). Un $()
vacío $()
inicia una subshell que se ejecuta correctamente y devuelve un código de salida de 0
:
$ if $() ; then echo yes ; else echo no ; fi
yes
Aaron planteó algunos puntos interesantes:
$ if $(echo true; false) ; then echo yes ; else echo no ; fi
yes
$ if $(echo ''true ; false'') ; then echo yes ; else echo no ; fi
yes
$ if true ; false ; then echo yes ; else echo no ; fi
no
$ if $(echo false ; true) ; then echo yes ; else echo no ; fi
no
$ if $(echo ''false ; true'') ; then echo yes ; else echo no ; fi
no
$ if false ; true ; then echo yes ; else echo no ; fi
yes
$
Parece que al ejecutar un comando construido a través de la subshell $()
, lo que importa es el estado de salida del primer comando.
Inspirado por esta pregunta :
¿Qué debe hacer una instrucción if cuando la condición es una sustitución de comando donde el comando no produce salida?
NOTA: El ejemplo es if $(true); then ...
if $(true); then ...
, no if true ; then ...
if true ; then ...
Por ejemplo, dado:
if $(true) ; then echo yes ; else echo no ; fi
Pensaría que $(true)
debería ser reemplazado por la salida del comando true
, que no es nada. Entonces debería ser equivalente a esto:
if "" ; then echo yes ; else echo no ; fi
que no
imprime porque no hay ningún comando cuyo nombre sea la cadena vacía, o para esto:
if ; then echo yes ; else echo no ; fi
que es un error de sintaxis.
Pero el experimento muestra que si el comando no produce salida, la sentencia if
trata como verdadero o falso según el estado del comando, en lugar de su salida.
Aquí hay un script que demuestra el comportamiento:
#!/bin/bash
echo -n ''true: '' ; if true ; then echo yes ; else echo no ; fi
echo -n ''false: '' ; if false ; then echo yes ; else echo no ; fi
echo -n ''$(echo true): '' ; if $(echo true) ; then echo yes ; else echo no ; fi
echo -n ''$(echo false): '' ; if $(echo false) ; then echo yes ; else echo no ; fi
echo -n ''$(true): '' ; if $(true) ; then echo yes ; else echo no ; fi
echo -n ''$(false): '' ; if $(false) ; then echo yes ; else echo no ; fi
echo -n ''"": '' ; if "" ; then echo yes ; else echo no ; fi
echo -n ''(nothing): '' ; if ; then echo yes ; else echo no ; fi
y aquí está la salida que obtengo (Ubuntu 11.04, bash 4.2.8):
true: yes
false: no
$(echo true): yes
$(echo false): no
$(true): yes
$(false): no
"": ./foo.bash: line 9: : command not found
no
./foo.bash: line 10: syntax error near unexpected token `;''
./foo.bash: line 10: `echo -n ''(nothing): '' ; if ; then echo yes ; else echo no ; fi''
Las primeras cuatro líneas se comportan como yo esperaría; Las líneas $(true)
y $(false)
son sorprendentes.
Un experimento adicional (no se muestra aquí) indica que si el comando entre $(
y )
produce una salida, su estado de salida no afecta el comportamiento del if
.
Veo un comportamiento similar (pero diferentes mensajes de error en algunos casos) con bash
, ksh
, zsh
, ash
y dash
.
No veo nada en la documentación de bash, o en la especificación POSIX "Shell Command Language", para explicar esto.
(O quizás me esté perdiendo algo obvio).
EDITAR: A la luz de la respuesta aceptada, aquí hay otro ejemplo del comportamiento:
command='''' ; if $command ; then echo yes ; else echo no ; fi
o equivalente:
command= ; if $command ; then echo yes ; else echo no ; fi
Consulte la sección 2.9.1 de la especificación de idioma . La última oración de la primera sección dice:
Si hay un nombre de comando, la ejecución continuará como se describe en Búsqueda y ejecución de comandos. Si no hay un nombre de comando, pero el comando contenía una sustitución de comando, el comando se completará con el estado de salida de la última sustitución de comando realizada. De lo contrario, el comando se completará con un estado de salida cero.
El $(true)
está expandiendo a la cadena vacía. El shell analiza la cadena vacía y encuentra que no se da ningún comando y sigue la regla anterior.
Hay potencialmente dos códigos de salida a considerar. Primero, aquí hay otros dos experimentos que deberían ayudar:
# if $(echo true; false) ; then echo yes ; else echo no ; fi
yes
El comando interno sale con el fracaso debido a lo false
. Pero eso es irrelevante porque la salida del comando no está vacía y, por lo tanto, la salida ("true") se ejecuta en su lugar y su código de salida tiene prioridad.
# if $(echo false; true) ; then echo yes ; else echo no ; fi
no
Nuevamente, la línea de comando dentro de $( )
es exitosa, pero la salida es no porque la salida ("falso") tiene prioridad.
El estado de salida de los comandos dentro de $( )
es relevante si y solo si la salida está vacía o es solo un espacio en blanco. Si la salida está vacía, no hay una lista de comandos que ejecutar y, por lo tanto, parece que el shell volverá al estado de salida del comando interno .
Lo que parece estar sucediendo es que bash
mantiene el estado de salida del último comando ejecutado
Esto explicaría por qué $(true)
y $(false)
tienen un comportamiento diferente en una prueba if
. Ambos producen comandos nulos que no cuentan como ejecución pero tienen códigos de salida diferentes.
Tan pronto como usa la sustitución de comandos en un comando que tiene salida, $()
intenta ejecutar esa salida como un comando y el código de salida de ese intento es ahora el último utilizado para la prueba if