Asignación de código de salida a una variable local de shell

42
#!/bin/bash
function0()
{
 local t1=$(exit 1)
 echo $t1
}

function0

echoImprime el valor vacío. Esperaba:

1

¿Por qué a la t1variable no se le asigna el valor de retorno del comando de salida 1?

Gilles 'SO- deja de ser malvado'
fuente

Respuestas:

58

local t1=$(exit 1) le dice al shell que:

  • ejecutar exit 1en una subshell;
  • almacenar su salida (como en el texto que envía a la salida estándar) en una variable t1, local a la función.

Por lo tanto, es normal que t1termine vacío.

( $()se conoce como sustitución de comando ).

El código de salida siempre está asignado a $?, por lo que puede hacer

function0()
{
  (exit 1)
  echo "$?"
}

para obtener el efecto que estás buscando. Por supuesto, puede asignar $?a otra variable:

function0()
{
  (exit 1)
  local t1=$?
  echo "$t1"
}
Stephen Kitt
fuente
1
Ya sabes, siempre puedes poner el retorno en la tubería también. `$ (trap 'printf" :: ERRNO: $? "' 0; # ahora haga lo que sea, esa trampa asegurará que la última cadena escrita sea el último retorno para todo el contexto de sustitución.
mikeserv
1
@mikeserv, ¿te perdiste un backtick? $(trap 'printf "::ERRNO:$?"' 0; # now do whatever however
Doktor J
12

El código de salida se almacenó en $? variable. Usando la sustitución de comandos solo captura la salida, debe usar (...) para crear subshell :

#!/bin/bash

func() {
  (exit 1)
  local t1=$?
  printf '%d\n' "$t1"
}

func
Cuonglm
fuente
El objetivo de la tarea t1=$?es usarlo, ¿no? y no quedaría $?golpeado por la operación de asignación? Creo que estoy preguntando si no debería serprintf '%d\n' "${t1}"
Dani_l
@Dani_l: Gracias, es un error tipográfico. Actualizado.
Cuonglm
Tenga en cuenta que la sustitución de comandos solo captura la salida estándar a menos que se redirija de manera diferente.
phyatt
7

En bashesto funciona:

loc(){  local   "x=$(exit "$1"):$?"
        printf  '$%s:\t%d\n' \
                 x "${x##*:}" \? "$?"
}

Tiene que ver con el orden de evaluación de comandos y asignación de variables. localtiene un valor de retorno propio, y es el comando que se está ejecutando actualmente, no la sustitución del comando. La razón por la que cosas como ...

x=$(exit 1); echo "$?"

... puede devolver 1 es porque nunca hay un retorno en ese comando, excepto por la ejecución de la subshell para asignar $xel valor, por lo $?que no se bloquea como lo hace prácticamente en cualquier otro caso en el que se usan sustituciones de comandos.

De todos modos, con la localque no consigue clobbered - pero si lo detecta en el momento justo - que es, mientras que las expansiones aún están siendo evaluados y antes de local rutinas 's tienen la oportunidad de darle una paliza que - todavía se puede asignarla.

unset x; loc 130; echo "${x-\$x is unset}"

...huellas dactilares...

$x: 130
$?: 0
$x is unset

Sin embargo, debe saber que en muchos shells no puede confiar en que $?se establezca a mitad de la evaluación de esa manera. De hecho, probablemente se deba a que esos proyectiles no se molestan en reevaluar en cada coyuntura posible, tal vez lo bashhace, lo cual diría que es probablemente un mejor comportamiento que bashel de ellos. ¿Realmente desea que su intérprete evalúe recursivamente valores de bucle que es muy probable que se sobrescriban antes de tener la oportunidad de usarlos?

De todos modos, así es como puedes hacer eso.

mikeserv
fuente
-1

Dependiendo de por qué está tratando de obtener el código de salida, también puede ejecutarlo, if some-command; then echo "Success $?"; else echo "Failure $?"; filo que no hace nada con la salida del comando, solo evalúa el código de salida de la ejecución del comando. Puede agregar or( or$ ( around the command and you'll still get the same results. A better example might besi grep -q 'somestring' algún archivo; luego repita "¿El código de salida de somestring encontrado es $?"; De lo contrario, "¿No encontró el código de salida de somestring es $?"; Fi`.

También puede probar el código de retorno de una función que puede ser un return 3código de retorno explícito o implícito que es el resultado del último comando, en este caso debe tener cuidado de no tener un echoal final del función, de lo contrario enmascara / restablece el código de salida anterior.

command_last () {
  echo "True is `true`"
  echo "False is `false`"
  false
}
command_last; echo $?
# Outputs:
# True is 0
# False is 1
# 1

echo_last () {
  echo "True is `true`"
  echo "False is `false`"
  false
  # echo'ing literally anything (or nothing) returns true aka exit 0
  echo
}
echo_last; echo $?
# Outputs:
# True is 0
# False is 1
#            # Blank line due to empty echo
# 0

Finalmente, un truco sucio ya que no puedes hacerlo VAR=(SOME_COMMAND)porque VAR=()es una definición de matriz, por lo que debes hacerlo VAR=( $(echo 'Some value') ).

dragon788
fuente
Todos los resultados reclamados son incorrectos, debido al hecho de que la sustitución de comandos no da el código de salida, que es el punto central de la pregunta. No está claro qué tiene que ver el "truco sucio" con nada.
Nick Matteo