¿Retorno implícito en funciones bash?

11

Digamos que tengo una función bash así:

gmx(){
  echo "foo";
}

¿Esta función devolverá implícitamente el valor de salida del echocomando, o está usando return necesario?

gmx(){
  echo "foo";
  return $?
}

Supongo que de la forma en que funciona bash, el estado de salida del comando final de la función bash es el que se "devuelve", pero no es 100% seguro.

Alexander Mills
fuente

Respuestas:

10

returnrealiza un retorno explícito de una función de shell o "script de puntos" (un script de origen). Si returnno se ejecuta, se realiza un retorno implícito al final de la función de shell o script de puntos.

Si returnse ejecuta sin un parámetro, es equivalente a devolver el estado de salida del comando ejecutado más recientemente.

Así es como returnfunciona en todos los shells POSIX.

Por ejemplo,

gmx () {
  echo 'foo'
  return "$?"
}

es por lo tanto equivalente a

gmx () {
  echo 'foo'
  return
}

que es lo mismo que

gmx () {
  echo 'foo'
}

En general, es muy raro que necesite usarlo $?. Realmente solo es necesario si necesita guardarlo para uso futuro, por ejemplo, si necesita investigar su valor varias veces (en cuyo caso asignaría su valor a una variable y realizaría una serie de pruebas en esa variable).

Kusalananda
fuente
2
Una desventaja de usar returnes que para funciones definidas como f() (...; cmd; return), evita la optimización que hacen algunos shells al ejecutar cmdel mismo proceso que el subshell. Con muchos shells, eso también significa que el estado de salida de la función no contiene la información que cmdse eliminó cuando la tiene (la mayoría de los shells no pueden recuperar esa información de todos modos).
Stéphane Chazelas
1
Tenga en cuenta que con pdksh y algunos de sus derivados (como OpenBSD sho posh), necesitaría return -- "$?"si existiera la posibilidad de que el último comando fuera una función que devolviera un número negativo. mksh(también basado en pdksh) prohíbe que las funciones devuelvan valores negativos.
Stéphane Chazelas
gracias esto ayuda, supongo que no entiendo cómo return xfunciona de manera diferente que exit x... lo único que sé es que return xno saldrá del proceso actual.
Alexander Mills
@AlexanderMills Bueno, es lo que dije: returnse usa para regresar de una función o un script de puntos. exithace algo completamente diferente (termina un proceso).
Kusalananda
Sí, eso tiene sentido. Creo que estoy empezando a manejarlo mejor
Alexander Mills
7

Desde la bash(1)página del manual:

Cuando se ejecuta, el estado de salida de una función es el estado de salida del último comando ejecutado en el cuerpo.

Ignacio Vazquez-Abrams
fuente
¿correcto, y un corolario podría ser que la declaración de devolución no es más que el estado de salida?
Alexander Mills,
Supongo que returnes una orden interna - aunque return 1es diferente a exit 1, etc., así
Alexander Mills
1
"return [n]: hace que una función deje de ejecutarse y devuelva el valor especificado por n a la persona que llama. Si se omite n, el estado de retorno es el del último comando ejecutado en el cuerpo de la función". (ibid) Entonces, returnfuerza el estado de salida de una función a un valor específico si se especifica.
Ignacio Vazquez-Abrams
1
@AlexandwrMills Sí, returny exitambos están integrados, excepto returnque solo se pueden usar dentro de la función. No puede terminar un script con return. El estado de salida es el valor que devuelve el comando. returnes un comando que devuelve ese valor. Entonces, "la declaración de retorno no es más que el estado de salida" simplemente no es del todo precisa. Uno es un valor, el otro es comando más valor.
Sergiy Kolodyazhnyy
1
@AlexanderMills, returnregresa de la función, exitsale del shell completo. Es exactamente lo mismo que en, digamos C con returnvs. exit(n), o returnvs. sys.exit()en Python.
ilkkachu
2

Solo agregaré algunas notas de precaución a las respuestas ya proporcionadas:

  • Aunque returntiene un significado muy especial para el shell, desde el punto de vista de la sintaxis, es un comando incorporado del shell y una instrucción return se analiza como cualquier otro comando simple. Entonces, eso significa que, como en el argumento de cualquier otro comando, $?cuando no se cita, estaría sujeto a split + glob

    Entonces necesita citar eso $?para evitarlo:

    return "$?"
  • returnpor lo general no acepta ninguna opción ( ksh93's acepta la costumbre --help, --man, --author... sin embargo). El único argumento que espera (opcional) es el código de retorno. El rango de códigos de retorno aceptados varía de shell a shell, y si algún valor fuera de 0..255 se refleja adecuadamente $?también varía de shell a shell. ¿Ver el código de salida predeterminado cuando finaliza el proceso? para detalles sobre eso.

    La mayoría de los shells aceptan números negativos (después de todo, el argumento pasado a la llamada _exit()/ exitgroup()system es an int, por lo que los valores abarcan al menos -2 31 a 2 31 -1, por lo que solo tiene sentido que los shells acepten el mismo rango para sus funciones) .

    La mayoría de los proyectiles usan el waitpid()y co. API para recuperar ese estado de salida, sin embargo, en cuyo caso, se trunca a un número entre 0 y 255 cuando se almacena en $?. Aunque invocar una función no implica generar un proceso y se utiliza waitpid()para recuperar su estado de salida, ya que todo se hace en el mismo proceso, muchos shells también imitan ese waitpid()comportamiento al invocar funciones. Lo que significa que incluso si llama returncon un valor negativo, $?contendrá un número positivo.

    Ahora, entre esos shells que returnaceptan números negativos (ksh88, ksh93, bash, zsh, pdksh y derivados distintos de mksh, yash), hay algunos (pdksh y yash) que necesitan escribirse como return -- -123si no -123se toman como tres -1, -2, -3opciones no válidas

    Como pdksh y sus derivados (como OpenBSD sho posh) conservan el número negativo $?, eso significa que return "$?"fallaría cuando $?contiene un número negativo (lo que sucedería cuando el último comando de ejecución fuera una función que devolviera un número negativo).

    Entonces return -- "$?"sería mejor en esas conchas. Sin embargo, tenga en cuenta que, aunque es compatible con la mayoría de los shells, esa sintaxis no es POSIX y, en la práctica, no es compatible con mkshderivados de cenizas.

    En resumen, con shells basados ​​en pdksh, puede usar números negativos en argumentos para funciones, pero si lo hace, return "$@"no funcionará. En otros shells, return "$@"funcionará y debe evitar el uso de números negativos (o números fuera de 0..255) como argumentos para return.

  • En todos los shells que conozco, llamar returndesde dentro de un subshell que se ejecuta dentro de una función hará que el subshell salga (con el estado de salida proporcionado si existe o el del último comando ejecutado), pero de lo contrario no causará un retorno de la función ( para mí, no está claro si POSIX le otorga esa garantía, algunos argumentan que exitdebería usarse en lugar de salir de subcapas dentro de las funciones). Por ejemplo

    f() {
      (return 3)
      echo "still inside f. Exit status: $?"
    }
    f
    echo "f exit status: $?"

    dará salida:

    still inside f. Exit status: 3
    f exit status: 0
Stéphane Chazelas
fuente
0

Sí, el valor de retorno implícito de una función es el estado de salida del último comando ejecutado . Eso también es cierto en cualquier punto de cualquier script de shell. En cualquier punto de la secuencia de ejecución del script, el estado de salida actual es el estado de salida del último comando ejecutado. Incluso comando ejecutado como parte de una asignación de variables: var=$(exit 34). La diferencia con las funciones es que una función podría cambiar el estado de salida al final de la ejecución de la función.

La forma alternativa de cambiar el "estado de salida actual" es iniciar un shell secundario y salir con cualquier estado de salida necesario:

$ $(exit 34)
$ echo "$?"
34

Y sí, es necesario citar la expansión del estado de salida :

$ IFS='123'
$ $(exit 34)
$ echo $?
4

A (exit 34)también trabajo.
Algunos pueden argumentar que una construcción más robusta debería ser $(return 34), y que una salida debería "salir" del script que se está ejecutando. Pero $(return 34)no funciona con ninguna versión de bash. Por lo tanto, no es portátil.

La forma más segura de establecer un estado de salida es usarlo tal como fue diseñado para trabajar, definir y returndesde una función:

exitstatus(){ return "${1:-"$?"}"; }

Entonces, al final de una función. es exactamente equivalente a tener nada o returno return "$?". El final de una función no necesita significar la "última línea de código de una función".

#!/bin/sh
exitstatus(){ a="${1:-"$?"}"; return "$a"; }
gmx(){
    if     [ "$1" = "one" ]; then
           printf 'foo ';
           exitstatus 78
           return "$?"
    elif   [ "$1" = "two" ]; then
           printf 'baz ';
           exitstatus 89
           return
    else
           printf 'baz ';
           exitstatus 90
    fi
}  

Imprimirá:

$ ./script
foo 78
baz 89
baz 90

El único uso práctico para "$?"es imprimir su valor: echo "$?"o almacenarlo en una variable (ya que es un valor efímero y cambiar con cada comando ejecutado): exitstatus=$?(recuerde citar la variable en comandos como export EXITSTATUS="$?".

En el returncomando, el rango de valores válido es generalmente de 0 a 255, pero comprenda que 126 + nalgunos shells usan valores de para indicar un estado de salida especial, por lo tanto, la recomendación general es usar 0-125.

Isaac
fuente