Diferencia entre retorno y salida en funciones Bash

429

¿Cuál es la diferencia entre la declaración returny exiten las funciones Bash con respecto a los códigos de salida?

lecodesportif
fuente
55
Protip: escriba help <command>su shell para obtener información sobre lo que hará un shell incorporado. En su caso help returnyhelp exit
SiegeX

Respuestas:

318

De en man bashadelante return [n];

Hace que una función deje de ejecutarse y devuelva el valor especificado por n a su llamador. Si se omite n, el estado de retorno es el del último comando ejecutado en el cuerpo de la función.

... en exit [n]:

Hacer que el shell salga con un estado de n. Si se omite n, el estado de salida es el del último comando ejecutado. Se ejecuta una trampa en EXIT antes de que finalice el shell.

EDITAR:

Según su edición de la pregunta, con respecto a los códigos de salida, returnno tiene nada que ver con los códigos de salida. Los códigos de salida están destinados a aplicaciones / scripts , no a funciones. Entonces, a este respecto, la única palabra clave que establece el código de salida del script (el que puede ser atrapado por el programa de llamada utilizando la $?variable de shell) es exit.

EDITAR 2:

Mi última declaración de referencia exitestá causando algunos comentarios. Fue hecho para diferenciar returny exitpara la comprensión del OP, y de hecho, en cualquier punto dado de un script de programa / shell,exit es la única forma de terminar el script con un código de salida para el proceso de llamada.

Toda orden ejecutada en la cáscara produce un "código de salida" local: establece la $?variable para ese código, y se puede utilizar con if, &&y otros operadores a condicionalmente ejecutar otros comandos.

Estos códigos de salida (y el valor de $? variable) se restablecen mediante la ejecución de cada comando.

Por cierto, el código de salida del último comando ejecutado por la secuencia de comandos se utiliza como el código de salida de la secuencia de comandos en sí, como se ve en el proceso de llamada.

Finalmente, las funciones, cuando se llaman, actúan como comandos de shell con respecto a los códigos de salida. El código de salida de la función ( dentro de la función) se establece mediante return. Entonces, cuando return 0se ejecuta una función , la ejecución de la función termina, dando un código de salida de 0.

Diego Sevilla
fuente
10
No exactamente. Siempre devuelve un valor del shell actual. No importa si estás dentro de una función o no.
Diego Sevilla
10
Comente sobre su edición: puedo confundir los valores de retorno y los códigos de salida, pero tiene un func(){ return 50; };func;echo $?eco de 50. Por lo tanto, la $?variable de shell no parece estar limitada a exit.
lecodesportif
8
"Se $?expande al estado de salida de la tubería en primer plano ejecutada más recientemente". Esa salida puede ser desde el shell en forma de una llamada a exit(o tocar el final del script) o en forma de una llamada a returnuna función.
SiegeX
11
@lecodesportif: La parte $? del proceso / script actual está limitada exital resultado del último comando ejecutado por este script. Entonces, si su última línea de script es la llamada a esa función, y esa función devuelve 50, sí, lo $?que produce para el proceso que lo llamó es 50. Sin embargo, eso no tiene que ver con el return, porque esto es restringido a la secuencia de comandos actual. Resulta que se devuelve solo si esta llamada a la función es la última oración del script. exit, sin embargo, siempre finalice el script y devuelva ese valor en cuanto $? al proceso de llamada .
Diego Sevilla
11
-1 por confundirme con la línea " returnno tiene nada que ver con los códigos de salida". La experimentación me dice que no hay diferencia funcional entre el código de retorno de una función y el código de salida de un script.
Jack
296

returnhará que la función actual quede fuera de alcance, mientras que exithará que el script finalice en el punto donde se llama. Aquí hay un programa de muestra para ayudar a explicar esto:

#!/bin/bash

retfunc()
{
    echo "this is retfunc()"
    return 1
}

exitfunc()
{
    echo "this is exitfunc()"
    exit 1
}

retfunc
echo "We are still here"
exitfunc
echo "We will never see this"

Salida

$ ./test.sh
this is retfunc()
We are still here
this is exitfunc()
SiegeX
fuente
17
Buen ejemplo También puede mostrar el valor de salida de 1 pulg $?.
Diego Sevilla
48
Tenga en cuenta que esta función NO imprimirá "Todavía estamos aquí" si agrega "set -e" antes de la llamada a "retfunc".
Michael
44
Sin embargo, echo fnord | while read x; do exitfunc; done; echo "still here"imprimirá "todavía aquí". Parece que solo se whilesale del sub-shell en este escenario.
tripleee
Una solución aproximada done || exit $?es fea y no es exactamente equivalente.
tripleee
2
+1 Puede ser útil agregar: `` `` returnhará que la función actual o el script de origen se salga del alcance '' `` .
Isaac
56

No creo que nadie haya respondido completamente la pregunta porque no describe cómo se usan los dos. OK, creo que sabemos que exit mata el script, donde quiera que se llame y puede asignarle un estado, como exit o exit 0 o exit 7 y así sucesivamente. Esto se puede utilizar para determinar cómo se forzó la detención del script si otro script lo invoca, etc. Suficiente al salir.

return cuando se llama devolverá el valor especificado para indicar el comportamiento de la función, generalmente un 1 o un 0. Por ejemplo:

    #!/bin/bash
    isdirectory() {
      if [ -d "$1" ]
      then
        return 0
      else
        return 1
      fi
    echo "you will not see anything after the return like this text"
    }

verifica así:

    if isdirectory $1; then echo "is directory"; else echo "not a directory"; fi

o así:

    isdirectory || echo "not a directory"

En este ejemplo, la prueba se puede usar para indicar si se encontró el directorio. tenga en cuenta que nada después de la devolución no se ejecutará en la función. 0 es verdadero pero falso es 1 en el shell, diferente de otros programas.

Para obtener más información sobre funciones: http://www.linuxjournal.com/content/return-values-bash-functions

NOTA: La función isdirectory es solo para fines de instrucción. Esta no debería ser la forma en que realiza dicha opción en un script real.

Mike Q
fuente
3
O simplemente utilícelo test -d $1para lograr el mismo resultado. Nunca lo hagas if <check> return else return. <check>solo haré lo mismo en todos los idiomas que conozco al menos.
erikbwork
44
Para ser aún más explícito sobre lo que dice erik: isdirectory() { [ -d "$1" ]; }se comportará exactamente igual que lo que tiene aquí: el valor de retorno predeterminado de una función de shell, ya sea al llegar al final de su código o por un returnsin argumentos, es el del comando más reciente
Charles Duffy
11
Los otros comentaristas aquí están criticando el estilo del ejemplo de Mike Q, cuando realmente está hablando sobre el comportamiento de la returndeclaración. Es cierto que su ejemplo es simplista y no debe usarse en la producción. Pero es simple, por lo que cumple bien su tarea. No tiene nada de malo.
Mike S
Gracias Mike S, sí, estoy de acuerdo en que el ejemplo más simple explica mejor salida vs retorno. Los otros comentarios son ciertamente válidos, y deben considerarse para los codificadores de bash más avanzados ;-)
Mike Q
1
Algunos podrían decir que no está exactamente relacionado con la pregunta, pero se relacionó y respondió al problema que tuve que me llevó a encontrar esta pregunta. :-)
Jesse Steele
33

Recuerde, las funciones son internas a un script y normalmente regresan de donde fueron llamadas usando la instrucción return. Llamar a un script externo es algo completamente diferente, y los scripts generalmente terminan con una declaración de salida.

La diferencia "entre la declaración de retorno y de salida en las funciones de BASH con respecto a los códigos de salida" es muy pequeña. Ambos devuelven un estado, no valores per se. Un estado de cero indica éxito, mientras que cualquier otro estado (1 a 255) indica un error. La declaración de retorno volverá al script desde donde se llamó, mientras que la declaración de salida finalizará el script completo desde donde se encuentre.

return 0  # returns to where the function was called.  $? contains 0 (success).

return 1  # returns to where the function was called.  $? contains 1 (failure).

exit 0  # exits the script completely.  $? contains 0 (success).

exit 1  # exits the script completely.  $? contains 1 (failure).

Si su función simplemente termina sin una declaración de devolución, el estado del último comando ejecutado se devuelve como el código de estado (y se colocará en $?).

Recuerde, regresar y salir devuelva un código de estado de 0 a 255, disponible en $? . No puede introducir nada más en un código de estado (por ejemplo, devolver "cat"); no funcionará. Pero, una secuencia de comandos puede transmitir 255 razones diferentes de falla mediante el uso de códigos de estado.

Puede establecer variables contenidas en el script de llamada, o hacer eco de los resultados en la función y usar la sustitución de comandos en el script de llamada; pero el propósito de regresar y salir es pasar códigos de estado, no valores o resultados de cálculo como cabría esperar en un lenguaje de programación como C.

usuario2100135
fuente
25

A veces, ejecutas un script usando .o source.

. a.sh

Si incluye un exiten el a.sh, no solo terminará el script, sino que finalizará su sesión de shell.

Si incluye un returnen el a.sh, simplemente deja de procesar el script.

Juraj
fuente
1
Pero cuando solo ejecuto a.sh obtengo un error return: can only 'return' from a function or sourced script, lo que lo hace inadecuado para un script general.
Peter - Restablece a Monica el
En el nivel superior de un script, ninguno es adecuado en allsituaciones. Usando .o sourceejecuta el script en el shell actual, en lugar de generar un sub-shell. El guión tiene que saber cómo se debe usar. Ay del usuario que lo hace enfrente. Personalmente, recomiendo leer los scripts antes de ejecutarlos por primera vez.
Jesse Chisholm
3
Un truco increíble que encontré es usar una trapfunción ERR EXITy luego primero guardar el código de salida de un comando fallido errCode=$?y luego salir del script (de origen o no) con return $errCode || exit $errCodelos ||medios "si no puedo regresar porque no fui de origen , solo salga ".
dragon788
6

En palabras simples (principalmente para novatos en codificación), podemos decir,

`return` : exits the function,
`exit()` : exits the program(called as process while running)

También si observaste, esto es muy básico pero ...,

`return` : is the keyword
`exit()` : is the function
Jyo the Whiff
fuente
1
En un script bash, exitno es más o menos una función que return. Son comandos incorporados. Ni siquiera son palabras reservadas.
Peter - Restablece a Monica el
6
  • exitterminar el proceso actual ; con o sin código de salida, considere este un sistema más que una función de programa. Tenga en cuenta que al buscar, exitfinalizará el shell, sin embargo, cuando se ejecute, solo se ejecutará exitel script.

  • returndesde una función, vuelva a las instrucciones después de la llamada, con o sin código de retorno. returnes opcional y está implícito al final de la función. returnsolo se puede usar dentro de una función.

Quiero agregar que mientras se obtiene, no es fácil para exitel script desde una función sin matar el shell. Creo que un ejemplo es mejor en un script de 'prueba'

#!/bin/bash
function die(){
   echo ${1:=Something terrible wrong happen}
   #... clean your trash
   exit 1
}

[ -f /whatever/ ] || die "whatever is not available"
# now we can proceed
echo "continue"

haciendo lo siguiente:

user$ ./test
Whatever is not available
user$

test -y- el caparazón se cerrará.

user$ . ./test
Whatever is not available

solo testterminará y se mostrará el mensaje.

La solución es incluir el posible procedimiento en (y)

#!/bin/bash
function die(){
   echo $(1:=Something terrible wrong happen)
   #... clean your trash
   exit 1
}

( # added        
    [ -f /whatever/ ] || die "whatever is not available"
    # now we can proceed
    echo "continue"
) # added

ahora, en ambos casos solo testsaldrá.

fcm
fuente
Agregar el (y )pone ese bloque en un sub-shell, deshaciendo efectivamente el .comando (fuente) como si hubiera ejecutado el script de prueba normalmente, que está en un sub-shell. Si el script no se ejecuta .o sourcetiene efectivamente 2 subcapas .
Jesse Chisholm
3

La pregunta del OP: ¿Cuál es la diferencia entre la declaración de retorno y de salida en las funciones de BASH con respecto a los códigos de salida?

En primer lugar, se requiere alguna aclaración:

  • No se requiere una declaración (retorno | salida) para terminar la ejecución de una (función | shell). Una (función | shell) terminará cuando llegue al final de su lista de códigos, incluso sin una declaración (retorno | salida).
  • No se requiere una declaración (retorno | salida) para devolver un valor desde una terminada (función | shell). Cada proceso tiene una variable incorporada $? que siempre tiene un valor numérico. Es una variable especial que no se puede establecer como "? = 1", pero se establece solo de manera especial (ver más abajo *). El valor de $? después del último comando que se ejecutará en (llamado función | sub shell) es el valor que se devuelve al (llamador de función | shell principal). Eso es cierto si el último comando ejecutado es ("return [n]" | "exit [n]") o plain ("return" u otra cosa que resulta ser el último comando en el código de funciones llamado.

En la lista de viñetas anterior, elija entre "(x | y)", ya sea siempre el primer elemento o siempre el segundo elemento para obtener declaraciones sobre funciones y retorno o shells y salir respectivamente.

Lo que está claro es que ambos comparten el uso común de la variable especial $? para pasar valores hacia arriba después de que terminen.

* Ahora por las formas especiales que $? se puede ajustar:

  • Cuando una función llamada finaliza y vuelve a la llamada, entonces $? en la persona que llama será igual al valor final de $? en la función terminada.
  • Cuando un shell primario espera de manera implícita o explícita en un solo sub shell y se libera al finalizar ese sub shell, entonces $? en el shell padre será igual al valor final de $? en el sub shell finalizado.
  • Algunas funciones integradas pueden modificar $? dependiendo de su resultado. Pero algunos no.
  • Funciones incorporadas "retorno" y "salida", cuando seguido de un argumento numérico tanto $? con argumento, y termina la ejecución.

Vale la pena señalar que $? se le puede asignar un valor llamando a exit en un sub shell, como este:

# (exit 259)
# echo $?
3  
Craig Hicks
fuente
44
En caso de que algunos lo perdieron, exit 259ecos como 3debido a que el valor de salida final es un solo byte. 259 % 256 = 3
Jesse Chisholm
0

En primer lugar, returnes una palabra clave y exitmi amigo es una función.

Dicho esto, aquí hay una explicación más simple.

return Devuelve un valor de una función.

exit Sale o abandona el caparazón actual.

Ahmad Awais
fuente
¡Realmente no! Estás lógicamente equivocado. Salir es una función mientras que returnes una palabra clave. El retorno es mucho más que solo códigos de salida, por lo que la comparación no es justa.
Ahmad Awais
Lo he editado para aclarar el punto que estaba tratando de aclarar. Gracias por ayudarme a hacer eso.
Ahmad Awais
44
Ni exittampoco returnson "palabras clave" o, como las llama el manual de bash, "palabras reservadas". Ninguno de los dos es una "función" tampoco, en el sentido de una función bash. Ambos son comandos incorporados, en bash lingo. (Se llama una función de biblioteca estándar C exit(), y el lenguaje de programación C tiene una palabra reservada return, pero no debe confundirse con los comandos bash, a pesar de que su semántica es curiosamente similar.)
Peter - Vuelva a instalar Monica el