Muitos scripts Bash são escritos em um estilo estritamente imperativo: execute um comando, depois execute outro comando, e assim por diante. Às vezes você precisa agrupar uma sequência de comandos que, conceitualmente, cumprem um único propósito. É aí que as funções entram.
Você define uma função assim:
my_function () {
COMMANDS
}
O conjunto vazio de parênteses apenas indica que você está definindo uma função. Nada vai dentro deles.
Uma vez definidas, as funções agem como qualquer outro comando (interno ou não).
Como qualquer comando, você pode passar argumentos para suas funções.
Dentro das funções, você acessa os argumentos usando os parâmetros posicionais, $1, $2, etc.
(Lembre-se: aprendemos sobre parâmetros posicionais no conceito Variáveis.)
O parâmetro especial $0 não muda dentro de uma função; ele continua sendo o nome do script em execução.
A função em execução no momento pode acessar seu próprio nome com a variável $FUNCNAME.
Veja 3.4.2 Special Parameters no manual.
Você pode definir variáveis dentro de uma função.
Se você declarar as variáveis com o comando local, o escopo da variável fica limitado à função atual (e a quaisquer funções chamadas por ela).
Caso contrário, a variável fica no escopo global.
Variáveis locais podem ter o mesmo nome de uma variável global. Nesse caso, a variável local "sombreia" a variável global. Por exemplo, uma variável local declarada em uma função esconde uma variável global de mesmo nome: referências e atribuições se referem à variável local, deixando a variável global inalterada. Quando a função retorna, 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, você pode acessar variáveis do escopo do chamador. Isso significa que você pode usar variáveis globais, bem como variáveis locais que foram declaradas no chamador (ou em alguma função que chama o chamador).
Tecnicamente, "global" não é a palavra mais adequada. Para expandir uma variável em uma função, o Bash sobe pela pilha de chamadas, até o escopo global, para encontrar uma função em que aquele nome de variável tenha sido declarado.
Este exemplo foi adaptado da seção Shell Functions 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 vai atribuí-lo no escopo onde ela foi declarada. Essa "ação à distância" pode gerar código difícil de acompanhar, já que nem sempre é óbvio onde uma variável recebeu um valor.
A pilha de chamadas pode ser examinada usando a variável de array FUNCNAME.
Uma função, como qualquer comando, tem um status de saída. Por padrão, o status de uma função é o status de saída do último comando executado.
Você pode usar o comando return para sair de uma função com um status 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 retorna o status do último comando executado.
Repare que a função check_password pode ser simplificada para:
check_password () { [[ $1 == "secret" ]]; }
[[...]] tem um status de saída: 0 para "verdadeiro", 1 para "falso".{...} precisa de uma quebra de linha ou um ponto e vírgula antes da chave de fechamento.O status de retorno de uma função é só um número. Como uma função pode produzir saída?
Sua função pode imprimir na saída padrão. Use a conhecida substituição de comando para capturá-la:
d6 () { echo "$(( 1 + RANDOM % 6 ))"; }
die=$( d6 )
echo "You rolled a $die."
O status de saída de uma função fica disponível para uso mesmo quando você está capturando 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
Funções podem chamar a si mesmas recursivamente. Por padrã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)