Retorno implícito nas funções do bash?

11

Digamos que eu tenho uma função bash assim:

gmx(){
  echo "foo";
}

Essa função retornará implicitamente o valor de saída do echocomando ou o retorno é necessário?

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

Suponho que, da maneira como o bash funciona, o status de saída do comando final da função bash é aquele que é "retornado", mas não 100% certo.

Alexander Mills
fonte

Respostas:

10

returnfaz um retorno explícito de uma função de shell ou "script de ponto" (um script de origem). Se returnnão for executado, um retorno implícito é feito no final da função shell ou script de ponto.

Se returnfor executado sem um parâmetro, é equivalente a retornar o status de saída do comando executado mais recentemente.

É assim que returnfunciona em todos os shells POSIX.

Por exemplo,

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

é, portanto, equivalente a

gmx () {
  echo 'foo'
  return
}

que é o mesmo que

gmx () {
  echo 'foo'
}

Em geral, é muito raro você precisar usá $?-lo. É realmente necessário apenas se você precisar salvá-lo para uso futuro, por exemplo, se precisar investigar seu valor várias vezes (nesse caso, você atribuiria seu valor a uma variável e executaria uma série de testes nessa variável).

Kusalananda
fonte
2
Uma desvantagem do uso returné que, para funções definidas como f() (...; cmd; return), isso impede a otimização que alguns shells fazem de executar cmdo mesmo processo que o subshell. Com muitas shells, isso também significa que o status de saída da função não carrega as informações que cmdforam mortas quando as possui (a maioria das shells não pode recuperar essas informações de qualquer maneira).
Stéphane Chazelas
1
Observe que, com o pdksh e alguns de seus derivados (como o OpenBSD shou posh), você precisaria return -- "$?"se houvesse uma chance de que o último comando fosse uma função que retornasse um número negativo. mksh(também baseado em pdksh) proíbe funções de retornar valores negativos.
Stéphane Chazelas
graças isso ajuda, acho que não entendo como as return xfunções funcionam de maneira diferente de exit x... a única coisa que sei é que return xnão sairá do processo atual.
Alexander Mills
@AlexanderMills Bem, foi o que eu disse: returné usado para retornar de uma função ou de um script de ponto. exitfaz algo completamente diferente (finaliza um processo).
Kusalananda
Sim, isso faz sentido. Acho que estou começando a lidar melhor com isso #
Alexander Mills
7

Na bash(1)página do manual:

Quando executado, o status de saída de uma função é o status de saída do último comando executado no corpo.

Ignacio Vazquez-Abrams
fonte
certo, e um corolário pode ser que a declaração de retorno nada mais seja do que o status de saída?
Alexander Mills
Eu acho que returné um comando builtin - embora return 1é diferente exit 1, etc assim
Alexander Mills
1
"return [n]: Faz com que uma função pare de executar e retorne o valor especificado por n ao seu chamador. Se n for omitido, o status de retorno será o do último comando executado no corpo da função." (ibid) Portanto, returnforça o status de saída de uma função a um valor específico, se especificado.
Ignacio Vazquez-Abrams
1
@AlexandwrMills Sim, returne exitsão ambos integrados, exceto returnque só podem ser usados ​​na função. Você não pode finalizar um script com return. Status de saída é o valor que o comando retorna. returné um comando que retorna esse valor. Portanto, "a declaração de retorno nada mais é do que o status de saída" simplesmente não é muito preciso. Um é um valor, o outro é comando mais valor.
Sergiy Kolodyazhnyy 28/05
1
@AlexanderMills, returnretorna da função, exitsai de todo o shell. É exatamente o mesmo que, digamos C com returnvs. exit(n)ou returnvs. sys.exit()em Python.
Ilkkachu 28/05
2

Vou apenas adicionar algumas notas de cautela às respostas já fornecidas:

  • Embora returntenha um significado muito especial para o shell, do ponto de vista da sintaxe, é um comando interno do shell e uma instrução de retorno é analisada como qualquer outro comando simples. Então, isso significa que, como no argumento de qualquer outro comando, $?quando não citado, estaria sujeito a split + glob

    Então, você precisa citar isso $?para evitá-lo:

    return "$?"
  • returngeralmente não aceita qualquer opção ( ksh93's aceita o habitual --help, --man, --author... embora). O único argumento esperado (opcional) é o código de retorno. O intervalo de códigos de retorno aceitos varia de shell para shell, e se qualquer valor fora de 0..255 é refletido corretamente $?também varia de shell para shell. Consulte Código de saída padrão quando o processo for finalizado? para detalhes sobre isso.

    A maioria das shells aceita números negativos (afinal, o argumento passado para a chamada _exit()/ exitgroup()system é um int, portanto, com valores que abrangem pelo menos -2 31 a 2 31 -1, portanto, faz sentido que shells aceitem o mesmo intervalo para suas funções) .

    A maioria das conchas usa o waitpid()e co. API para recuperar esse status de saída no entanto nesse caso, é truncado para um número entre 0 e 255, quando armazenada em $?. Mesmo que invocar uma função não envolva a geração de um processo e usado waitpid()para recuperar seu status de saída, pois tudo é feito no mesmo processo, muitos shells também imitam esse waitpid()comportamento ao invocar funções. O que significa que, mesmo se você ligar returncom um valor negativo, $?conterá um número positivo.

    Agora, entre as conchas cuja returnaceita números negativos (ksh88, ksh93, bash, zsh, pdksh e outros que mksh derivados, Yash), existem alguns (pdksh e yash), que precisa dele escrito como return -- -123se de outra forma que -123é tomado como três -1, -2, -3opções inválidas.

    Como o pdksh e seus derivados (como o OpenBSD shou posh) preservam o número negativo $?, isso significa que a execução return "$?"falharia quando $?contém um número negativo (o que aconteceria quando o último comando de execução fosse uma função que retornasse um número negativo).

    Então return -- "$?"seria melhor nessas conchas. No entanto, observe que, embora suportado pela maioria dos shells, essa sintaxe não é POSIX e, na prática, não é suportada por mkshe derivados de ash.

    Portanto, para resumir, com shells baseados em pdksh, você pode usar números negativos em argumentos para funções, mas se o fizer, return "$@"não funcionará. Em outros shells, return "$@"funcionará e você deve evitar usar números negativos (ou números fora de 0..255) como argumentos para return.

  • Em todas as shells que eu conheço, chamar returnde dentro de um subshell executando dentro de uma função fará com que o subshell saia (com o status de saída fornecido, se houver algum ou o do último comando executado), mas não causará um retorno da função ( para mim, não está claro se o POSIX oferece essa garantia, alguns argumentam que exitdeve ser usado em vez de sair dos subshells dentro das funções). Por exemplo

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

    irá produzir:

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

Sim, o valor de retorno implícito de uma função é o status de saída do último comando executado . Isso também é verdade em qualquer ponto de qualquer script de shell. Em qualquer ponto da sequência de execução do script, o status de saída atual é o status de saída do último comando executado. Mesmo comando executado como parte de uma atribuição de variável: var=$(exit 34). A diferença com as funções é que uma função pode alterar o status de saída no final da execução da função.

A maneira alternativa de alterar o "status de saída atual" é iniciar um sub shell e sair com qualquer status de saída necessário:

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

E sim, a expansão do status de saída precisa ser citada:

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

Um (exit 34)trabalho também.
Alguns podem argumentar que uma construção mais robusta deve ser $(return 34)e que uma saída deve "sair" do script que está sendo executado. Mas $(return 34)não funciona com nenhuma versão do bash. Portanto, não é portátil.

A maneira mais segura de definir um status de saída é usá-lo como foi projetado para trabalhar, definir e a returnpartir de uma função:

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

Então, no final de uma função. é exatamente equivalente a ter nada ou returnou return "$?". O final de uma função não precisa significar a "última linha de código de uma função".

#!/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
}  

Irá imprimir:

$ ./script
foo 78
baz 89
baz 90

O único uso prático para isso "$?"é imprimir seu valor: echo "$?"ou armazená-lo em uma variável (pois é um valor efêmero e muda a cada comando executado): exitstatus=$?(lembre-se de citar a variável em comandos como export EXITSTATUS="$?".

No returncomando, o intervalo de valores válido geralmente é de 0 a 255, mas entenda que os valores de 126 + nsão usados ​​por alguns shells para sinalizar um status de saída especial; portanto, a recomendação geral é usar 0-125.

Isaac
fonte