Retornando valores de funções
Esta aula explica como retornar valores de funções em shell script, abordando a diferença entre exit status e saída, a captura de saída com substituição de comando, o uso de variáveis globais e os padrões recomendados para evitar efeitos colaterais.
Em shell script, funções podem produzir resultados de duas formas principais: através do exit status (código de retorno) ou através da saída padrão (stdout). Nesta aula, vamos explorar essas abordagens, entender como capturar saídas de funções, o papel das variáveis globais e os padrões para escrever funções limpas e previsíveis.
Dominar o retorno de valores é essencial para criar scripts modulares e reutilizáveis. Vamos ver na prática cada técnica com exemplos.
Exit status vs saída
O exit status é um valor inteiro entre 0 e 255 que uma função (ou comando) retorna para indicar sucesso (0) ou falha (qualquer valor diferente de 0). Esse valor é acessado pela variável especial $? imediatamente após a execução da função. Já a saída é qualquer texto escrito na stdout (usando echo, printf, etc.), que pode ser capturado com substituição de comando.
Muitos iniciantes confundem os dois. O exit status é usado para controle de fluxo (testar sucesso/fracasso), enquanto a saída é usada para transmitir dados. Uma função pode retornar um exit status e também produzir saída, mas é importante ter clareza sobre qual mecanismo usar em cada situação.
#!/bin/bash
minha_funcao() {
echo "Mensagem na stdout"
return 42
}
minha_funcao
echo "Exit status: $?"
# A saída será:
# Mensagem na stdout
# Exit status: 42
Capturando saída
Para capturar a saída de uma função (ou comando), usamos a substituição de comando com $(comando) ou com crases `comando`. A saída é armazenada em uma variável. Isso permite que a função retorne strings ou dados complexos.
#!/bin/bash
saudacao() {
local nome="$1"
echo "Olá, $nome!"
}
mensagem=$(saudacao "Maria")
echo "$mensagem" # Olá, Maria!
É possível capturar múltiplas linhas de saída. A variável conterá exatamente o que foi escrito na stdout, incluindo quebras de linha. Para preservar espaços e quebras, sempre coloque a variável entre aspas duplas ao usá-la.
#!/bin/bash
lista() {
echo "item1"
echo "item2"
echo "item3"
}
itens=$(lista)
echo "$itens"
# item1
# item2
# item3
Uma observação importante: a substituição de comando captura apenas a stdout. A stderr não é capturada a menos que seja redirecionada (ex.: $(comando 2>&1)).
Variáveis globais
Outra forma de retornar valores é modificar variáveis globais dentro da função. Embora seja simples, essa prática pode causar efeitos colaterais indesejados, pois a função altera o estado do script fora dela. Por isso, é recomendado usar variáveis locais (local) para evitar poluição do escopo global.
#!/bin/bash
resultado="" # variável global
calcula() {
resultado=$(( $1 + $2 ))
}
calcula 10 20
echo "Resultado: $resultado" # Resultado: 30
O uso de variáveis globais pode ser aceitável em scripts pequenos, mas em projetos maiores é preferível capturar a saída ou usar variáveis de retorno com nomes específicos (ex.: _resultado). Uma boa prática é declarar a variável como local e depois atribuí-la a uma variável global explicitamente, se necessário.
#!/bin/bash
calcula() {
local soma=$(( $1 + $2 ))
# opcional: atribuir a uma variável global
resultado_global="$soma"
}
calcula 5 7
echo "Global: $resultado_global" # Global: 12
Padrões
Existem padrões consagrados para retornar valores de funções em shell script. Os mais comuns são:
- Retornar via stdout: Use
echoouprintfpara enviar o resultado e capture com$(). É a forma mais flexível e recomendada para dados não-numéricos ou complexos. - Retornar via variável de referência: Passe o nome de uma variável como argumento e use
evalouprintf -vpara atribuir. Exemplo:atribuir() { printf -v "$1" "%s" "$2"; }. - Retornar via variável global controlada: Use uma variável com prefixo (ex.:
return_) para indicar que é um valor de retorno. A função deve documentar que modifica essa variável. - Retornar exit status: Use
returnpara códigos de erro/sucesso. Nunca usereturnpara dados não-inteiros.
#!/bin/bash
# Padrão 1: stdout
obter_nome() {
echo "João"
}
nome=$(obter_nome)
echo "Nome: $nome"
# Padrão 2: variável de referência
set_var() {
printf -v "$1" "%s" "valor atribuído"
}
minha_var=""
set_var minha_var
echo "minha_var: $minha_var"
# Padrão 3: variável global
return_valor=""
funcao_global() {
return_valor="resultado global"
}
funcao_global
echo "return_valor: $return_valor"
Escolha o padrão de acordo com a necessidade. Para scripts que serão mantidos por outras pessoas, prefira a captura de stdout, pois é mais explícita e previsível.
Boas práticas
- Sempre use
localpara variáveis dentro de funções, a menos que precise explicitamente modificar uma variável global. - Evite
returnpara valores não-inteiros; use stdout. - Documente se a função modifica variáveis globais ou se espera argumentos especiais.
- Ao capturar saída, lembre-se de que a substituição de comando remove newlines no final. Use aspas duplas para preservar espaços.
- Para retornar múltiplos valores, considere imprimir cada valor em uma linha separada e depois ler com
readou array.
Referências
- GNU Bash Manual: Shell Functions
- GNU Bash Manual: Command Substitution
- BashFAQ/005: How can I use variable variables (indirect variables, pointers, references) or associative arrays?
- Stack Overflow: How to return a string value from a bash function
- Advanced Bash-Scripting Guide: Functions
Exercícios
Escreva uma função chamada
somaque recebe dois números, imprime a soma na stdout e retorna 0. Capture a saída em uma variável e exiba-a.✓ Resposta:#!/bin/bash soma() { echo $(($1 + $2)) return 0 } resultado=$(soma 3 4) echo "Soma: $resultado" # Soma: 7Crie uma função que retorna o maior de dois números usando o exit status: retorne 0 se o primeiro é maior, 1 se o segundo é maior, 2 se são iguais. Teste com exemplos.
✓ Resposta:#!/bin/bash maior() { if [ $1 -gt $2 ]; then return 0 elif [ $2 -gt $1 ]; then return 1 else return 2 fi } maior 5 3 echo "Exit: $?" # 0 maior 2 8 echo "Exit: $?" # 1 maior 4 4 echo "Exit: $?" # 2Escreva uma função que modifica uma variável global chamada
global_resultcom o dobro de um número passado. Em seguida, imprima o valor.✓ Resposta:#!/bin/bash global_result=0 dobro() { global_result=$(( $1 * 2 )) } dobro 7 echo "Global result: $global_result" # 14Crie uma função que retorna o nome do mês (string) a partir do número (1-12) usando
case. Capture a saída e exiba.✓ Resposta:#!/bin/bash mes_por_extenso() { case $1 in 1) echo "Janeiro";; 2) echo "Fevereiro";; 3) echo "Março";; 4) echo "Abril";; 5) echo "Maio";; 6) echo "Junho";; 7) echo "Julho";; 8) echo "Agosto";; 9) echo "Setembro";; 10) echo "Outubro";; 11) echo "Novembro";; 12) echo "Dezembro";; *) echo "Mês inválido";; esac } mes=$(mes_por_extenso 5) echo "Mês: $mes" # MaioImplemente uma função que retorna a soma e o produto de dois números. Use a saída padrão para retornar ambos (um por linha) e depois leia com
readem um array.✓ Resposta:#!/bin/bash soma_produto() { echo $(( $1 + $2 )) echo $(( $1 * $2 )) } IFS=$'\n' read -d '' -ra valores < <(soma_produto 3 4) soma="${valores[0]}" produto="${valores[1]}" echo "Soma: $soma, Produto: $produto" # Soma: 7, Produto: 12