Fu

Funções em Bash

{one: "1 exercício", many: "%{count} exercícios", other: "%{count} exercícios"}

Sobre Funções

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.

Definir uma função

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.

Parâmetros das funções

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

Advanced

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.

Variáveis

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

Advanced

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.

Advanced

A pilha de chamadas pode ser examinada com a variável de tipo array FUNCNAME.

Valores devolvidos

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.

Note

Repara que a função check_password pode ser simplificada para:

check_password () { [[ $1 == "secret" ]]; }
  1. A construção condicional [[...]] tem um estado de saída: 0 para "verdadeiro", 1 para "falso".
  2. A construção de agrupamento {...} tem de ter uma nova linha ou um ponto e vírgula antes da chaveta de fecho.

Saída da função

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

Usar a saída e o estado ao mesmo tempo

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

Recursão

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
Advanced

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)
Editar via GitHub A ligação abre numa nova janela ou separador