Retornando um valor de uma função bash

10

Eu tenho uma função que retorna 1 se o número for um número válido de dez dígitos:

valNum()
{
    flag=1
    if [[ $1 != [1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] ]]; then
        echo "Invalid Number"
        flag=0
    fi
    return $flag
}

Está sendo chamado por:

if [[ $(valNum $num) -eq 1 ]]; then
      #do something
fi

A função está funcionando bem se o número for válido, mas está mostrando um erro de sintaxe se você inserir um número inválido.

user2179293
fonte

Respostas:

14

A resposta de @ choroba está correta, no entanto, este exemplo pode ser mais claro:

valNum $num
valNumResult=$? # '$?' is the return value of the previous command
if [[ $valNumResult -eq 1 ]]
then
  : # do something
fi

Este exemplo é um pouco mais longo (definindo $valNumResulte consultando esse valor), mas descreve de forma mais explícita o que acontece: que valNum()retorna um valor e esse valor pode ser consultado e testado.

PS Por favor, faça um favor a si mesmo e retorne 0para truee diferente de zero para false. Dessa forma, você pode usar o valor de retorno para indicar "por que falhamos" no caso de falha.


fonte
8

As funções no bash podem retornar apenas códigos de saída. A substituição de comando, inversamente, é usada para obter a saída padrão de um comando ou função. Portanto, para verificar o sinalizador retornado, você não precisa da substituição:

if valNum "$num" ; then
    # ...
fi

Mas, para que funcione, você deve retornar 0 se o número for válido e 1 se não for (o código de saída 0 significa que não há erro).

choroba
fonte
Eu não entendo. No exemplo 24.7 em tldp.org/LDP/abs/html/complexfunct.html, a função está retornando o valor máximo e não o código de saída. Embora sua sugestão está funcionando, mas eu não sou capaz de entender por que ele está trabalhando
user2179293
1
como seu teste é descobrir se a entrada é um número inteiro válido de 10 dígitos ou não, ou seja, verdadeiro ou falso, a função retornará 0 ou 1. O exemplo de choroba funciona porque if valnum "$num"é equivalente a if valnum "$num" = 0"se for verdadeiro". A regra básica básica nos scripts sh é que 0 = verdadeiro / sucesso, diferente de zero = falso / erro.
cas
2
BTW, esse "Guia Avançado de Script Bash" não é um guia muito bom - ele está errado em muitas coisas e incentiva algumas práticas ruins de script. o FAQ do Bash em mywiki.wooledge.org/BashFAQ é um recurso muito melhor.
cas
sua função está falhando porque está ecoando a sequência "Número inválido" e, em seguida, você faz uma comparação numérica entre essa sequência e o número 1 comif [[ $(valNum $num) -eq 1 ]]
cas
5

Você não pode retornar um resultado arbitrário de uma função de shell. Você só pode retornar um código de status que seja um número inteiro entre 0 e 255. (Embora você possa passar um valor maior para return, ele é truncado no módulo 256.) O valor deve ser 0 para indicar sucesso e um valor diferente para indicar falha; por convenção, você deve manter códigos de erro entre 1 e 125, pois valores mais altos têm um significado especial (comando externo ruim para 126 e 127, interrompido por um sinal para valores mais altos).

Como você está retornando um resultado sim ou não aqui, um código de status é apropriado. Como flagparece indicar um sucesso ou falha, você deve usar os valores convencionais de 0 para sucesso e 1 para falha (o oposto do que você escreveu). Você pode usar sua função diretamente em uma instrução if.

valNum ()
{
  local flag=0
  if [[ $1 != [1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] ]]; then
    echo 1>&2 "Invalid Number"
    flag=1
  fi
  return $flag
}
if valNum "$num"; then
  #do something
fi

Se você precisar discriminar entre códigos de falha, chame a função diretamente. Imediatamente após o retorno, o código de falha está disponível em $?. Você pode verificá-lo com uma declaração de caso:

valNum "$num"
case $? in 

Se você precisar usar o código de status posteriormente, salve-o em outra variável antes de $?ser substituído pelo próximo comando.

valNum "$num"
valNum_status=$?

O que você escreveu não funcionou porque a substituição do comando se $(…)expande para a saída da função, que no seu código é a mensagem de erro ou está vazia, nunca 1.

Se você precisar passar mais informações do que um código de status permite que as funções fora do shell, você tem duas possibilidades:

  • Imprima algum texto na saída padrão e chame a função em uma substituição de comando: $(valNum "$num")
  • Atribua a uma ou mais variáveis ​​dentro da função e leia essas variáveis ​​mais tarde.
Gilles 'SO- parar de ser mau'
fonte
2

Eu mesmo tive resultados conflitantes nessa área. Aqui estão os resultados de minhas experiências empíricas. Primeiramente, alguma ' teoria ' sobre comandos bash ou * nix:

  • SUCESSO == 0 ... viz. nenhum código de status de erro)
  • FAIL! = 0 ...... algum código de status

Exemplo:

if  ls -lt /nonexistantdir
then 
    echo "found"
else
    echo "FAIL"
fi
#
echo
ls -lt /nonexistantdir; echo "status = $?"
echo "status = $?"

Resultado:

ls: cannot access '/nonexistantdir': No such file or directory
FAIL... 

ls: cannot access '/nonexistantdir': No such file or directory
status = 2

Como mostrado, o lscomando retorna o código de status = 2. Quando você tenta um diretório válido, o status é zero ( 0 ). Não é o mesmo que quase todos os outros idiomas.

regra # 1 - Faça ...

  • TRUE == 0
  • FALSO! = 0

Devemos lembrar que estamos testando códigos de erro em uma ifinstrução Bash . Eu configuro constantes ou você pode usar shell trueou falsecomandos.

TRUE=0
FALSE=1

#  valid number function
#
valNum()
{
    flag=$TRUE

    if [[ $1 != [1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] ]]; then
        echo "Invalid Number"
        flag=$FALSE
    fi
    return $flag
}

#    later on ...
#
if validNum Abc 
then
    echo "Lucky number"
else
    echo "Not lucky."
fi

e saída:

Invalid Number
Not lucky.

No entanto, sugiro que você dê @Gilles ' up-vote ' porque sua resposta é a correta. Eu só queria entender o lado simplista do ePaper.

Apenas mais uma coisa, o testcomando. Isso se parece com:

[[ some-expression ]]; 

A maior parte do tempo. E por exemplo:

$ test 1
$ echo "result = $?"
result = 0
$ test 0
$ echo "result = $?"
result = 0

Zero (0) sendo verdadeiro . Por quê? Bem, a página de manual diz que um único argumento é ' verdadeiro ' quando NÃO é NULL.

referências:

vai
fonte