Ultimamente tenho tido problemas estranhos com o bash. Ao tentar simplificar meu script, vim com esse pequeno pedaço 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
deveria ter saído da função sem imprimir $?
, não deveria? Bem, então verifiquei se posso retornar de um cano sozinho:
$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script
O mesmo acontece sem um while
loop:
$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.
Há algo que estou perdendo aqui? Uma pesquisa no Google não trouxe nada sobre isso! Minha versão do bash é a versão 4.2.37 (1) no Debian Wheezy.
bash
shell-script
pipe
subshell
Teresa e Junior
fonte
fonte
while
não for necessário para a reprodução? Isso distrai do ponto.while
loop é um uso muito comum para um pipereturn
. O segundo exemplo é mais direto ao ponto, mas é algo que eu não acredito que alguém jamais usaria ...Respostas:
Relacionado: /programming//a/7804208/4937930
Não é um bug que você não possa sair de um script ou retornar de uma função por
exit
oureturn
em subshells. Eles são executados em outro processo e não afetam o processo principal.Além disso, suponho que você esteja vendo comportamentos não documentados de bash em (provavelmente) especificações indefinidas. Em uma função, nenhum erro é declarado
return
no nível superior dos comandos de subshell e apenas se comporta comoexit
.IMHO é um bug de bash para o comportamento inconsistente de
return
depender se a declaração principal está em uma função ou não.Resultado:
fonte
return
não funciona a partir de uma sequência de comandos de nível superior em um subshell, e em particular não sai do subshell, é o que os documentos existentes já me fizeram esperar. O OP pode usarexit 1 || return 1
onde eles estão tentando usarreturn
e, em seguida, deve obter o comportamento esperado. EDIT: A resposta de @ herbert indica que o nível superiorreturn
no subshell está funcionando comoexit
(mas somente no subshell).return
em uma sequência simples de subshell deve ser declarada como um erro de tempo de execução em qualquer caso , mas na verdade não é quando ocorre em uma função. Esse problema também foi discutido no gnu.bash.bug , mas não há conclusão.return
instrução está em uma função e, portanto, é legal. O comportamento resultante, no entanto, não é especificado.Não é um bug,
bash
mas seu comportamento documentado :A
return
instrução é válida estando dentro de uma definição de função, mas estando em um subshell, ela não afeta seu shell pai, portanto a próxima instruçãoecho
é executada independentemente. No entanto, é uma construção de shell não portátil, pois o padrão POSIX permite que os comandos que compõem um pipeline sejam executados em uma subshell (o padrão) ou na parte superior (uma extensão permitida).Felizmente, você pode dizer
bash
para se comportar da maneira que espera com algumas opções:fonte
return
não encerra a função, não faria mais sentido se o shell fosse impressobash: return: can only `return' from a function or sourced script
, em vez de dar ao usuário uma falsa sensação de que a função pode ter retornado?return
de um subshell. Afinal, ele sabe que está em um subshell, como evidenciado pela$BASH_SUBSHELL
variável. O maior problema é que isso pode levar a falsos positivos; um usuário que entenda como os subshells funcionam pode ter scripts escritos que são usadosreturn
para substituirexit
um subshell. (E, claro, há casos válidos onde se pode querer variáveis fixos ou fazer umcd
em um subnível.)return
está retornando do subshell em vez de falhar, pois está dentro de uma função real. O problema é quehelp return
afirma especificamente:Causes a function or sourced script to exit with the return value specified by N.
Ao ler a documentação, qualquer usuário esperaria que ao menos falhasse ou imprimisse um aviso, mas nunca se comportasse dessa maneiraexit
.return
em um subshell em uma função retorne da função (no processo principal do shell) não entende muito bem os subshells. Por outro lado, eu esperaria que um leitor que entenda subconjuntos esperereturn
em um subconjunto em uma função finalizá-lo, exatamente comoexit
faria.De acordo com a documentação do POSIX, o uso de
return
fora da função ou do script de origem não é especificado . Portanto, depende do seu shell para lidar.O shell SystemV relatará erro, enquanto estiver dentro
ksh
,return
fora da função ou do script de origem se comportar comoexit
. A maioria das outras conchas POSIX e osh de schily também se comportam assim:ksh
ezsh
não produziu porque a última parte do pipe nessas conchas foi executada no shell atual em vez do subshell. A instrução de retorno afetou o ambiente de shell atual que chamou a função, faz com que a função retorne imediatamente sem imprimir nada.Na sessão interativa,
bash
apenas reporte o erro, mas não encerrou o shell,schily's osh
relatou o erro e encerrou o shell:(
zsh
Na sessão interativa e saída é fazer terminal não encerrado,bash
,yash
eschily's osh
relatou o erro, mas não encerrar o shell)fonte
return
é usado dentro de uma função aqui.return
foi usado dentro da função interna do subshell , exceto e .ksh
zsh
Eu acho que você obteve o comportamento esperado, no bash, cada comando em um pipeline é executado em um subshell. Você pode se convencer tentando modificar uma variável global de sua função:
A propósito, o retorno está funcionando, mas retorna do subshell. Mais uma vez, você pode verificar se:
Produzirá o seguinte:
Então a instrução return saiu corretamente do subshell
.
fonte
foo(){ : | return 1 || return 2; echo$?; echo "This should not be printed.";}; foo; echo $?
e você obterá um resultado2
. Mas para a clareza que eu faria oreturn 1
sejaexit 1
.A resposta mais geral é que o bash e algumas outras conchas normalmente colocam todos os elementos de um pipeline em processos separados. Isso é razoável quando a linha de comando é
já que os programas normalmente são executados em processos separados de qualquer maneira (a menos que você diga ). Mas pode ser uma surpresa para
exec program
onde alguns ou todos os comandos são comandos internos. Exemplos triviais incluem:
Um exemplo um pouco mais realista é
onde o todo
while
...do
...done
laço é colocado em um subprocesso, e assim suas alteraçõest
não são visíveis para o shell principal após as extremidades do laço. E é exatamente isso que você está fazendo - canalizando em umwhile
loop, fazendo com que o loop seja executado como um subshell e, em seguida, tentando retornar do subshell.fonte