Está documentado (para POSIX) en la Sección 2.9.1 Comandos simples
de las especificaciones básicas del grupo abierto. Hay un muro de texto allí; Dirijo su atención al último párrafo:
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.
Así por ejemplo,
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
Así es como funciona bash también. Pero vea también la sección "no tan simple" al final.
phk , en su pregunta ¿Las asignaciones son como comandos con un estado de salida, excepto cuando hay sustitución de comandos? , sugiere
... parece que una asignación en sí cuenta como un comando ... con un valor de salida cero, pero que se aplica antes del lado derecho de la asignación (por ejemplo, una llamada de sustitución de comando ...)
Esa no es una forma terrible de verlo. Un esquema de crudo para determinar el estado de retorno de un simple comando (uno que no contenga ;
, &
, |
, &&
o ||
) es:
- Escanee la línea de izquierda a derecha hasta llegar al final o una palabra de comando (generalmente un nombre de programa).
- Si ve una asignación variable, el estado de retorno de la línea podría ser 0.
- Si ve una sustitución de comando, es decir,
$(…)
tome el estado de salida de ese comando.
- Si llega a un comando real (no en una sustitución de comando), tome el estado de salida de ese comando.
El estado de retorno de la línea es el último número que encontró.
Las sustituciones de comandos como argumentos del comando, por ejemplo, foo $(bar)
no cuentan; obtienes el estado de salida de foo
. Parafraseando la notación de phk , el comportamiento aquí es
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Pero esto es una ligera simplificación excesiva. El estado general de devolución de
A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
es el estado de salida de . La asignación que ocurre después de la asignación no establece el estado general de salida en 0.
cmd4
E=
D=
icarus , en su respuesta a la pregunta de phk , plantea un punto importante: las variables se pueden establecer como de solo lectura. El penúltimo párrafo de la Sección 2.9.1 del estándar POSIX dice:
Si alguna de las asignaciones de variables intenta asignar un valor a una variable para la cual el atributo de solo lectura se establece en el entorno de shell actual (independientemente de si la asignación se realiza en ese entorno), se producirá un error de asignación de variable. Consulte las Consecuencias de los errores de Shell para conocer las consecuencias de estos errores.
entonces si dices
readonly A
C=Garfield A=Felix T=Tigger
el estado de retorno es 1. No importa si las cadenas Garfield
, Felix
y / o Tigger
se reemplazan con sustituciones de comando, pero vea las notas a continuación.
La sección 2.8.1 Consecuencias de los errores de Shell tiene otro grupo de texto y una tabla, y termina con
En todos los casos que se muestran en la tabla donde se requiere que un shell interactivo no salga, el shell no realizará ningún procesamiento adicional del comando en el que ocurrió el error.
Algunos de los detalles tienen sentido; algunos no:
- La
A=
asignación a veces aborta la línea de comando, ya que esa última oración parece especificar. En el ejemplo anterior, C
se establece en Garfield
, pero T
no se establece (y, por supuesto, tampoco se establece A
).
- Del mismo modo, se
ejecuta
pero no . Sin embargo, en mis versiones de fiesta (que incluyen 4.1.xy 4.3.x), que hace ejecutar . (Por cierto, esto impugna aún más la interpretación de phk de que el valor de salida de la asignación se aplica antes del lado derecho de la asignación).
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Pero aquí hay una sorpresa:
En mis versiones de bash,
solo lectura A
C = algo A = algo T = algo cmd 0
no ejecutar. En particular,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
ejecuta
y , pero no . (Tenga en cuenta que esto es lo contrario de su comportamiento cuando no hay un comando). Y se establece (así como ) en el entorno de . Me pregunto si esto es un error en bash.
cmd1
cmd3
cmd2
T
C
cmd0
No es tan simple:
El primer párrafo de esta respuesta se refiere a "comandos simples".
La especificación dice:
Un "comando simple" es una secuencia de asignaciones y redirecciones de variables opcionales, en cualquier secuencia, opcionalmente seguida de palabras y redirecciones, terminadas por un operador de control.
Estas son declaraciones como las de mi primer bloque de ejemplo:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
los primeros tres incluyen asignaciones variables y los últimos tres incluyen sustituciones de comandos.
Pero algunas asignaciones variables no son tan simples.
bash (1) dice:
Las sentencias de asignación también pueden aparecer como argumentos a la alias
, declare
, typeset
, export
, readonly
, y local
ÓRDENES INTERNAS ( declaración de comandos).
Para export
, la especificación POSIX dice:
ESTADO DE SALIDA
0 0Todos los operandos de nombre se exportaron con éxito.
> 0No se pudo exportar al menos un nombre, o -p
se especificó la opción y se produjo un error.
Y POSIX no es compatible local
, pero bash (1) dice:
Es un error usar local
cuando no está dentro de una función. El estado de retorno es 0 a menos que local
se use fuera de una función, se proporcione un nombre no válido o el nombre sea una variable de solo lectura.
Leyendo entre líneas, podemos ver que los comandos de declaración como
export FOO=$(bar)
y
local FOO=$(bar)
son más como
foo $(bar)
la medida en que ignoran el estado de salida desde bar
y le dan un estado de salida basándose en la orden principal ( export
, local
o foo
). Entonces tenemos rarezas como
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
que podemos demostrar con
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
y
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
Afortunadamente, ShellCheck detecta el error y levanta SC2155 , que informa que
export foo="$(mycmd)"
debe cambiarse a
foo=$(mycmd)
export foo
y
local foo="$(mycmd)"
debe cambiarse a
local foo
foo=$(mycmd)
local
vincula esto? Por ejemplolocal foo=$(bar)
?