Aula 15 – Funções

Aula 14 – Estruturas de decisão ‘if’ e ‘case’ | Índice


Seu apoio é muito importante para a criação e a manutenção dos cursos gratuitos do canal debxp:


15.1 – Conceito

Você pode imaginar uma função como um pequeno script dentro do seu script. Elas são pequenos blocos de comandos que recebem um nome pelo qual podem ser chamados. Com as funções, nós evitamos escrever os mesmos comandos e instruções várias vezes no script. Além disso, elas podem tornar o código mais legível, já que o nome da função (se bem escolhido) dá uma dica da finalidade daquele bloco de comandos.

15.2 – Sintaxe geral

No Bash, esses blocos de comandos podem ser qualquer comando composto (qualquer um mesmo!), mas o mais comum é utilizarmo o agrupamento de comandos com chaves.

nome_da_função() {
    COMANDOS
}

Ou, em uma linha…

nome_da_função() { COMANDOS; }

Importante! Como delimitadores de um agrupamento, as chaves são tratadas como palavras-chaves pelo shell, o que nos obriga a seperá-las de outras palavras da linha de comando. Além disso, no caso da escrita em uma linha, o último comando do agrupamento precisa terminar com o ponto e vírgula

Também podemos criar funções utilizando a palavra reservada function

function nome_da_função {
    COMANDOS
}

function nome_da_função { COMANDOS; }

A sintaxe é simples, mas alguns detalhes devem ser observados:

  • Criar uma função não faz com que ela seja executada. Para isso, é necessário invocá-la (nós dizemos "chamar") a partir de seu nome em algum ponto do script ou pelo terminal.

  • As funções devem aparecer no script antes dos pontos em que são chamadas, a menos que sejam chamadas por outras funções.

  • As regras para os nomes das funções são as mesmas dos nomes das variáveis e nós devemos sempre escolher nomes que descrevam bem a finalidade da função.

  • Em outras linguagens, nós podemos declarar argumentos dentro dos parêntesis que vêm depois do nome da função. No Bash, isso não é possível, e eles servem apenas para indicar que estamos definindo uma função quando não usamos a palavra reservada function.

15.3 – Nossa primeira função

Vamos criar um script chamado exemplo.sh:

#!/usr/bin/env bash

oi() {
   echo "Oi, eu sou uma função!"
}

oi

Aqui, nós criamos uma função chamada oi. Entre as chaves, nós definimos que ela executará apenas o comando echo, mas poderia ser qualquer quantidade de comandos. Depois de definida, mais adiante no código, nós podemos chamar a função oi quantas vezes quisermos simplesmente escrevendo seu nome nos pontos em que ela precisar ser chamada.

Executando o script…

:~$ ./exemplo.sh
Oi, eu sou uma função!

15.4 – Passando "argumentos"

Se a nossa função precisar processar informações, será necessário passar para ela os dados que deverão ser processados, e isso é feito através de parâmetros posicionais. Como já dissemos, nós podemos imaginar as funções como pequenos scripts dentro do nosso script, e essa analogia também engloba a forma como os scripts recebem os parâmetros posicionais.

Então, vamos relembrar: como você passa os parâmetros para um script?

:~$ ./script.sh param1 param2 param3 ... paramN

Internamente, cada um desses parâmetros seria automaticamente passado para as variáveis especiais $1, $2, $3$N respectivamente.

No caso das funções, acontece exatamente a mesma coisa.

Veja o exemplo:

#!/usr/bin/env bash

oi() {
echo "Oi, eu sou $1!"
}

oi Goku
oi Vegeta
oi "a Bulma"

Executando…

:~$ ./exemplo.sh
Oi, eu sou Goku!
Oi, eu sou Vegeta!
Oi, eu sou a Bulma!
:~$

Também como nos scripts…

  • $# – Armazena o número de argumentos passados para a função
  • $* e $@– Armazenam todos os argumentos passados para a função

Quando uma função é executada, os parâmetros posicionais (menos o $0) e os parâmetros $#, $* e $@ são temporariamente redefinidos para receberem os argumentos passados para ela. Quando sua execução termina, esses parâmetros são restaurados para os valores originais da sessão do shell.

15.5 – Retornando valores

A maioria das linguagens de programação trabalha com o conceito de funções que retornam um valor. As funções do Bash não permitem isso, mas nós podemos fazer com que as nossas funções retornem um status de saída (limitado ao inteiros de 0 a 255) através do comando interno return, o qual pode ser lido posteriormente utilizando a variável especial $?.

O comando return é análogo ao comando exit. A única diferença é que o return encerra apenas a execução do bloco de comandos da função, enquanto o exit encerra a execução de todo o script.

Voltando ao nosso exemplo…

#!/usr/bin/env bash

oi() {
}
    echo "Oi, eu sou $1!"
    [[ -n $1 ]] && return 0 || return 1

oi Goku
echo "Status: $?"
oi
echo "Status: $?"

Executando…

:~$ ./exemplo.sh
Oi, eu sou Goku!
Status: 0
Oi, eu sou
Status: 1
:~$

Porém, a forma mais correta de obter valores a partir da execução de uma função é através das nossas velhas amigas substituições de comandos.

Veja o exemplo:

#!/usr/bin/env bash

oi() {
   echo "Oi, eu sou $1!"
}
mensagem=$(oi $1)

echo $mensagem

Repare que a variável $1 aparece duas vezes: dentro da função, recebendo o valor do argumento passado na chamada da função e, no corpo do script, recebendo o valor do argumento passado na linha de comando.

Executando…

:~$ ./exemplo.sh Goku
Oi, eu sou Goku!
:~$ ./exemplo.sh Vegeta
Oi, eu sou Vegeta!

Além das substituições de comandos, nós podemos nos valer o fato de que todas as variáveis no script são tratadas como globais, a menos que seja definido o contrário. Desta forma, em vez de tentarmos obter da função o retorno de um valor, nós podemos fazer com que ela defina (ou redefina) variáveis.

Por exemplo:

#!/usr/bin/env bash

oi() {
    mensagem="Oi, eu sou $1!"
}

oi $1
echo $mensagem

Executando…

:~$ ./exemplo.sh Goku
Oi, eu sou Goku!
:~$ ./exemplo.sh Vegeta
Oi, eu sou Vegeta!

15.6 – Escopo de variáveis

Repetindo: todas as variáveis no script são tratadas como globais, a menos que seja definido o contrário, e só é possível definir variáveis locais dentro de funções, o que fazemos utilizando a palavra reservada local antes do nome da variável.

nome_da_função() {
    ...
    local NOME=VALOR
    ...
}

Por exemplo:

#!/usr/bin/env bash

msg="Oi, eu sou a Bulma!"

oi() {
   local msg="Oi, eu sou $1!"
   echo $msg
}

oi $1
echo $msg

Executando:

:~$ ./exemplo.sh Goku
Oi, eu sou Goku!
Oi, eu sou a Bulma!
:~$

Repare que, mesmo existindo duas variáveis com o mesmo nome (msg), seus valores não se misturam, porque uma está definida como local dentro do bloco de comandos da função. Portanto, o comando echo da função "sabe" que estamos nos referindo à variável local msg, e não à sua “xará” global.

15.7 – A variável ‘FUNCNAME’

A nossa analogia das funções como “pequenos scripts dentro de um script” tem uma falha importante quando se trata de retornar o nome da função. A variável especial $0 armazena o nome do script em execução, e isso não vai mudar se ele for acessado de dentro de uma função. Contudo, o Bash nos oferece uma variável interna que armazena o nome de todas as funções em execução, trata-se da array FUNCNAME.

Em FUNCNAME, o elemento de índice 0 é o nome da função atualmente em execução, e a variável só existe enquanto houver alguma função sendo executada no shell.

Por exemplo:

#!/usr/bin/env bash

Goku() {
    echo "Oi, eu sou ${FUNCNAME[0]}!"
}

Vegeta() {
    echo "Eu sou ${FUNCNAME[0]}, seu verme!"
}
Goku
Vegeta

Executando:

:~$ ./exemplo.sh
Oi, eu sou Goku!
Eu sou Vegeta, seu verme!
:~$

Dica: não é a forma mais correta, mas, como o valor do índice 0 de uma array indexada sempre será retornado quando a acessamos apenas com $NOME_DA_ARRAY, nós podemos obter o nome da função apenas com $FUNCNAME, sem as chaves e o [0].

15.8 – Diferenciando funções de comandos com o mesmo nome

É possível nomear funções com o mesmo nome de comandos que nós usamos no terminal. Na verdade, embora possa causar certa confusão dentro de scripts, isso é muito comum quando estamos definindo "aliases" e funções no arquivo .bashrc com o propósito de customizar o comportamento de comandos.

Quando isso acontece, é possível diferenciar as chamadas às funções das chamadas aos comandos originais com o builtin command.

Veja o exemplo:

#!/usr/bin/env bash

function ls {
   echo "Oi, eu sou uma função!"
}

ls 
command ls

Que, resultaria em algo como…

:~$ ./exemplo.sh
Oi, eu sou uma função!
Desktop Documentos Downloads ...
:~$

Aqui, ls retornou a saída da função, mas command ls retornou os arquivos no diretório corrente.

Importante! Tome muito cuidado ao criar funções com nomes de comandos, pois isso pode causar grandes problemas!


Aula 14 – Estruturas de decisão ‘if’ e ‘case’ | Índice

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Post comment