Ú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
return
deberí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 while
bucle:
$ 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
while
no es necesario para la reproducción? Se distrae del punto.while
bucle 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
exit
oreturn
en 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
return
en 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
return
depender de si la declaración principal está en una función o no.Salida:
fuente
return
no 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 1
donde está tratando de usarreturn
, y luego debería obtener el comportamiento esperado. EDITAR: la respuesta de @ herbert indica que toplevelreturn
en subshell funciona comoexit
(pero solo desde el subshell).return
en 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.return
declaración está en una función y, por lo tanto, es legal. Sin embargo, el comportamiento resultante no está especificado.No es un error
bash
sino su comportamiento documentado :La
return
instrucció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
bash
que se comporte de la manera que espera con un par de opciones:fuente
return
que 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?return
un subshell. Después de todo, sí sabe que está en una subshell, como lo demuestra la$BASH_SUBSHELL
variable. 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 usanreturn
en lugar deexit
terminar un subshell. (Y, por supuesto, hay casos válidos en los que uno podría querer establecer variables o hacer unacd
en una subshell.)return
está regresando de la subshell en lugar de fallar, ya que está dentro de una función real. El problema es quehelp return
establece 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
.return
en 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 esperereturn
en una subshell en una función terminar la subshell, tal como loexit
haría.Según la documentación de POSIX, el uso
return
fuera 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
,return
fuera 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í:ksh
yzsh
no 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,
bash
solo informe el error pero no finalizó el shell,schily's osh
informó el error y finalizó el shell:(
zsh
en la sesión interactiva y la salida es terminal, no terminóbash
,yash
eschily's osh
informó el error pero no terminó el shell)fuente
return
se usa dentro de una función aquí.return
era su uso dentro de subnivel dentro de la función , exceptoksh
yzsh
.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 1
serexit 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 program
donde algunos o todos los comandos son comandos integrados. Los ejemplos triviales incluyen:
Un ejemplo un poco más realista es
donde todo
while
...do
... eldone
bucle se coloca en un subproceso, por lo que sus cambiost
no son visibles para el shell principal después de que finaliza el bucle. Y eso es exactamente lo que estás haciendo: conectando unwhile
bucle, haciendo que el bucle se ejecute como una subshell, y luego tratando de regresar de la subshell.fuente