Muitos scripts Bash são escritos num estilo estritamente imperativo: executar um comando, depois executar outro comando, e por aí adiante. Por vezes, precisas de agrupar uma sequência de comandos que, conceptualmente, cumprem um único objetivo. É aqui que entram as funções.
Defines uma função assim:
my_function () {
COMMANDS
}
O conjunto vazio de parênteses serve apenas para indicar que estás a definir uma função. Não se coloca nada lá dentro.
Depois de definidas, as funções comportam-se como qualquer outro comando (interno ou não).
Tal como qualquer comando, podes passar argumentos às tuas funções.
Dentro das funções, acedes aos argumentos através dos parâmetros posicionais, $1, $2, etc.
(Lembra-te de que aprendemos sobre os parâmetros posicionais no conceito Variáveis.)
O parâmetro especial $0 não é alterado dentro de uma função; continua a ser o nome do script em execução.
A função em execução pode aceder ao seu nome através da variável $FUNCNAME.
Consulta 3.4.2 Parâmetros especiais no manual.
Podes definir variáveis dentro de uma função.
Se declarares as variáveis com o comando local, o âmbito da variável fica limitado à função atual (e a todas as funções que esta chamar).
Caso contrário, a variável é colocada no âmbito global.
As variáveis locais podem ter o mesmo nome que uma variável global. Nesse caso, a variável local "oculta" a variável global. Por exemplo, uma variável local declarada numa função oculta uma variável global com o mesmo nome: as referências e as atribuições dizem respeito à variável local, deixando a variável global inalterada. Quando a função termina, a variável global volta a ficar visível.
x=5
myfunc () {
local x=100
echo "in my function, $x == 100"
}
echo "in the global scope, $x == 5"
myfunc
echo "back in the global scope, $x == 5"
A saída é:
in the global scope, 5 == 5
in my function, 100 == 100
back in the global scope, 5 == 5
Dentro de uma função, podes aceder a variáveis do âmbito de quem chama. Ou seja, podes usar variáveis globais, bem como variáveis locais declaradas em quem chama a função (ou em alguma função que chame quem chama).
Tecnicamente, "global" não é a palavra mais correta a usar. Para expandir uma variável dentro de uma função, o Bash percorre a pilha de chamadas, até ao âmbito global, à procura de uma função onde esse nome de variável tenha sido declarado.
Este exemplo foi adaptado da secção Funções da shell do manual:
func1() {
local var='func1 local'
func2
}
func2() {
echo "In func2, var = $var"
}
var=global
func1
func2
A saída é:
In func2, var = func1 local
In func2, var = global
Da mesma forma, atribuir um valor a uma variável atribui-o no âmbito onde foi declarada. Esta "ação à distância" pode criar código difícil de seguir, porque nem sempre é óbvio onde é que uma variável recebeu um valor.
A pilha de chamadas pode ser examinada com a variável de tipo array FUNCNAME.
Uma função, tal como qualquer comando, tem um estado de saída. Por predefinição, o estado de uma função é o estado de saída do último comando executado.
Podes usar o comando return para sair de uma função com um estado de saída específico.
check_password () {
if [[ $1 == "secret" ]]; then
return 0
else
return 1
fi
}
read -sp "Enter your password: " pass
if check_password "$pass"; then
echo "Correct!"
else
echo "Wrong password."
fi
Usar return sem argumentos devolve o estado do último comando executado.
Repara que a função check_password pode ser simplificada para:
check_password () { [[ $1 == "secret" ]]; }
[[...]] tem um estado de saída: 0 para "verdadeiro", 1 para "falso".{...} tem de ter uma nova linha ou um ponto e vírgula antes da chaveta de fecho.O estado de saída de uma função é apenas um número. Como pode então uma função produzir saída?
A tua função pode imprimir para a saída padrão. Usa a já conhecida substituição de comandos para a capturar:
d6 () { echo "$(( 1 + RANDOM % 6 ))"; }
die=$( d6 )
echo "You rolled a $die."
O estado de saída de uma função continua disponível mesmo quando estás a capturar a saída.
roll () {
local n=$1
if (( 4 <= n && n <= 20 )); then
echo "$(( 1 + RANDOM % n ))" # exit status is 0
else
return 1
fi
}
read -p "How many faces does your die have? " faces
if die=$( roll "$faces" ); then
echo "You rolled a $die."
else
echo "I can't roll a die with $faces faces."
fi
As funções podem chamar-se a si próprias de forma recursiva. Por predefinição, não há limite para a profundidade da recursão.
Um exemplo:
fibonacci() {
local n=$1
if (( n <= 1 )); then
echo "1"
else
local a=$(fibonacci "$(( n - 1 ))")
local b=$(fibonacci "$(( n - 2 ))")
echo "$(( a + b ))"
fi
}
for i in {1..10}; do fibonacci "$i"; done
# => 1
# => 2
# => 3
# => 5
# => 8
# => 13
# => 21
# => 34
# => 55
# => 89
A profundidade da recursão pode ser controlada com a variável FUNCNEST.
bash -c '
recur() {
echo $1
recur $(($1 + 1))
}
FUNCNEST=5
recur 1
'
1
2
3
4
5
environment: line 1: recur: maximum function nesting level exceeded (5)