Últimamente he tenido algunos problemas extraños con bash. Mientras intentaba simplificar mi script, se me ocurrió este pequeño fragmento de código:
$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1
returndebería haber salido de la función sin imprimir $?, ¿no? Bueno, luego verifiqué si puedo regresar solo de una tubería:
$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script
Lo mismo sucede sin un whilebucle:
$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.
¿Hay algo que me falta aquí? ¡Una búsqueda en Google no trajo nada sobre esto! Mi versión bash es la versión 4.2.37 (1) en Debian Wheezy.
bash
shell-script
pipe
subshell
Teresa e Junior
fuente
fuente

whileno es necesario para la reproducción? Se distrae del punto.whilebucle es un uso muy común para una tubería conreturn. El segundo ejemplo es más directo al punto, pero es algo que no creo que nadie pueda usar ...Respuestas:
Relacionado: /programming//a/7804208/4937930
No es un error que no pueda salir de un script o regresar de una función por
exitoreturnen subcapas. Se ejecutan en otro proceso y no afectan el proceso principal.Además de eso, supongo que está viendo comportamientos indocumentados de bash en (probablemente) especificaciones indefinidas. En una función, no se afirman errores
returnen el nivel superior de los comandos de subshell y simplemente se comporta comoexit.En mi humilde opinión, es un error bash por el comportamiento inconsistente de
returndepender de si la declaración principal está en una función o no.Salida:
fuente
returnno funciona desde una secuencia de comandos de nivel superior en una subshell, y en particular no sale de la subshell, es lo que los documentos existentes ya me han hecho esperar. El OP podría usarexit 1 || return 1donde está tratando de usarreturn, y luego debería obtener el comportamiento esperado. EDITAR: la respuesta de @ herbert indica que toplevelreturnen subshell funciona comoexit(pero solo desde el subshell).returnen una secuencia de subshell simple se debe afirmar como un error de tiempo de ejecución en cualquier caso , pero en realidad no es cuando ocurre en una fucntion. Este problema también se ha discutido en gnu.bash.bug , pero no hay conclusión.returndeclaración está en una función y, por lo tanto, es legal. Sin embargo, el comportamiento resultante no está especificado.No es un error
bashsino su comportamiento documentado :La
returninstrucción es válida al estar dentro de una definición de función pero al estar también en una subshell, no afecta a su shell principal, por lo que la siguiente instrucciónecho, se ejecuta independientemente. Sin embargo, es una construcción de shell no portátil ya que el estándar POSIX permite que los comandos que componen una tubería se ejecuten en un subshell (el predeterminado) o el superior (una extensión permitida).Con suerte, puede decir
bashque se comporte de la manera que espera con un par de opciones:fuente
returnque no saldrá de la función, ¿no tendría más sentido si el shell acaba de imprimirsebash: return: can only `return' from a function or sourced script, en lugar de darle al usuario una falsa sensación de que la función podría haber regresado?returnun subshell. Después de todo, sí sabe que está en una subshell, como lo demuestra la$BASH_SUBSHELLvariable. El mayor problema es que esto podría conducir a falsos positivos; un usuario que entiende cómo funcionan los subshells podría haber escrito scripts que se usanreturnen lugar deexitterminar un subshell. (Y, por supuesto, hay casos válidos en los que uno podría querer establecer variables o hacer unacden una subshell.)returnestá regresando de la subshell en lugar de fallar, ya que está dentro de una función real. El problema es quehelp returnestablece específicamente: alCauses a function or sourced script to exit with the return value specified by N.leer la documentación, cualquier usuario esperaría que al menos fallara o imprimiera una advertencia, pero que nunca se comportara de esa maneraexit.returnen una subshell en una función regrese de la función (en el proceso de shell principal) no entiende muy bien las subcapas. Por el contrario, esperaría que un lector que entiende las subcapas esperereturnen una subshell en una función terminar la subshell, tal como loexitharía.Según la documentación de POSIX, el uso
returnfuera de la función o el script de origen no está especificado . Entonces, depende de su shell para manejar.El shell de SystemV informará un error, mientras que dentro
ksh,returnfuera de la función o el script de origen se comporta comoexit. La mayoría de los otros shells POSIX y schily's osh también se comportan así:kshyzshno salió porque la última parte de la tubería en estos shells se ejecutó en el shell actual en lugar del subshell. La declaración return afectó el entorno actual del shell que llamó a la función, ya que la función regresa inmediatamente sin imprimir nada.En la sesión interactiva,
bashsolo informe el error pero no finalizó el shell,schily's oshinformó el error y finalizó el shell:(
zshen la sesión interactiva y la salida es terminal, no terminóbash,yasheschily's oshinformó el error pero no terminó el shell)fuente
returnse usa dentro de una función aquí.returnera su uso dentro de subnivel dentro de la función , exceptokshyzsh.Creo que obtuvo el comportamiento esperado, en bash, cada comando en una tubería se ejecuta en una subshell. Puede ser usted mismo tratando de modificar una variable global de su función:
Por cierto, el retorno está funcionando pero regresa desde la subshell. De nuevo puedes comprobar que:
Producirá lo siguiente:
Entonces, la declaración de retorno salió correctamente de la subshell
.
fuente
foo(){ : | return 1 || return 2; echo$?; echo "This should not be printed.";}; foo; echo $?y obtendrá un resultado de2. Pero para mayor claridad, haría elreturn 1serexit 1.La respuesta más general es que bash y algunos otros shells normalmente colocan todos los elementos de una tubería en procesos separados. Esto es razonable cuando la línea de comando es
ya que los programas normalmente se ejecutan en procesos separados de todos modos (a menos que usted lo indique ) Pero puede ser una sorpresa para
exec programdonde algunos o todos los comandos son comandos integrados. Los ejemplos triviales incluyen:
Un ejemplo un poco más realista es
donde todo
while...do... eldonebucle se coloca en un subproceso, por lo que sus cambiostno son visibles para el shell principal después de que finaliza el bucle. Y eso es exactamente lo que estás haciendo: conectando unwhilebucle, haciendo que el bucle se ejecute como una subshell, y luego tratando de regresar de la subshell.fuente