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.

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).


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.


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

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.