Bash Script – Funções
Neste artigo sobre as funções em BASH, pretendo demonstrar como usamos funções, de forma simples como é o objetivo destes artigos. No entanto pretendo dar o máximo de informação sobre as funções.
As funções em BASH, tem o mesmo objetivo das funções em qualquer outra linguagem de programação.
Executar uma ação que normalmente é repetitiva, como também separar o código para determinadas ações.
Neste caso evitamos escrever o mesmo código múltiplas vezes. A repetição de código leva a um aumento do código escrito no script. Como consequência destas repetições de código temos maior dificuldade em achar onde está o possível erro, pois temos o mesmo código varias vezes escrito pelo script. Se necessitarmos atualizar um determinado código, temos de fazer em todas as partes do script que contem o código. É difícil de fazer manutenção do código.
Quando usamos funções nos nossos scripts apenas escrevemos a função uma vez. Quando necessitarmos atualizar o código dessa função apenas fazemos em um local. Torna o script muito mais compreensível pois o código está dentro de funções em que cada uma delas desempenha uma determinada função. Podem ver um pequeno exemplo aqui do uso de funções em BASH. Os exemplos deste artigo estarão em anexo como de costume.
Declaração funções
Em BASH podemos declarar as funções nos nossos scripts de várias formas.
Em várias linhas que é a forma mais usada, porque é mais legível e intuitiva:
nome () {
comando1
comando2
comandoN
}
ou
function nome () {
comando1
comando2
comandoN
}
Como podemos ver acima, podemos usar a palavra reservada “function” “nome da função”, mas podemos usar apenas “nome da função”. Isto é permitido em BASH quando declaramos funções nos nossos scripts. No entanto não podemos usar apenas a palavra reservada “function” como nome da função. Pois esta é uma palavra reservada pela linguagem.
Também podemos declarar as funções em apenas uma única linha.
Esta forma já não é tão usada pois não é tão legível e intuitiva. Pode ser necessária quando queremos declarar uma ou mais funções e código para ser enviado em uma shell que só permita uma única linha de código de cada vez ou para mini-ficar o código a fim de o tornar de propósito menos legível.
function nome () { comando1; comando2; comandoN; }
ou
nome () { comando1; comando2; comandoN; }
Quando declaramos funções em apenas uma linha temos de ter alguns cuidados:
- Depois da chaveta “{” tem de insistir um espaço obrigatoriamente;
- Os comandos têm de ser separados por “;”
- Se existir mais código depois da função a chaveta que termina a função tem de ter em seguida “};” ponto e virgula.
Se ao declararmos a nossa função com um nome de um comando existente no sistema, o BASH chama a função do script e não o comando do sistema. A declarar funções temos de ter o cuidado de lhes dar um nome apropriado e tendo em conta as regras para os nomes que são os mesmos das variáveis. Leia este artigo onde descrevo mais sobre nomes de variáveis
A forma como declaramos as funções, irá depender das necessidades, e do que se pretende.
As funções têm de ser declaradas antes de serem usadas. Devemos declarar as mesmas antes de escrever o código que as irá usar:
# Declarar a funções
function a(){
comando1
comando2
comandoN
}
function b(){
comando1
comando2
comandoN
}
function c(){
comando1
comando2
comandoN
}
# Chamar as funções
a
b
c
As funções em BASH não podem ser vazias ou apenas com comentários, têm de ter algo para ser executado, criar uma variável escrever algo, retornar da função:
function vazia(){
}
Chamar as funções
Quando queremos trabalhar com funções, para alem de as declarar previamente, temos de as chamar no código, para que o código nelas contido seja executado.
# Declarar a função ABC
function ABC(){
comando1
comando2
comandoN
}
# Chamar a função ABC
ABC
Como podemos ver é declarada a função “ABC” e depois da função declarada chamamos a função “ABC”.
É desta forma que usamos as funções em BASH.
Passagem de argumentos
Podemos passar argumentos para as nossas funções, tal como fazemos com os outros comandos do sistema. Para tal temos de chamar a função com os argumentos pretendidos, separados por espaços.
# Declarar a função ABC
function ABC(){
comando1
comando2
comandoN
}
# Chamar a função ABC
ABC argumento1 argumento2 argumentoN
Podemos ver a forma de chamar a função com os argumentos desejados.
No entanto não basta chamar a função com argumentos, queremos ter acesso aos argumentos dentro da função.
Os argumentos chegam às funções tal como chegam ao script quando são passados argumentos.
Para isso usamos as variáveis especiais do BASH para ter acesso aos argumentos passados à função. Leia este artigo onde descrevo mais sobre as variáveis especiais em BASH.
- $* – Esta variável contem todos os valores dos “argumentos” que foram passados à função, quando a função foi chamada. Estes valores são separados por ” ” um espaço.
- $@ – Esta variável contem todos os valores dos “argumentos” que foram passados à função, quando a função foi chamada.
- $# – Esta variável contem o numero de argumentos passados à função. A contagem começa em um e não em zero. É um numero na base dez, decimal.
- $1,$2,$3,$… – As variáveis $1,$2,$3 e por ai adiante contêm o valor de cada argumento passado à função. A variável $1 contem o valor do argumento 1 e por ai adiante.
Usando estas variáveis, temos acesso aos argumentos passados à função.
nome(){
# Escreve todos os argumentos passados à função
echo $*
# Escreve todos os argumentos passados à função
echo $@
# Escreve o numero de argumentos passado à função
echo $#
# Escreve o valor do argumento 1
echo $1
# Escreve o valor do argumento 2
echo $2
# Escreve o valor do argumento 3
echo $3
}
nome arg1 arg2 arg3
Com estas variáveis temos acesso aos argumentos passados à função e podemos usa los dentro da função, com o fim de processar os dados dependendo dos argumentos que sejam passados.
Nos exemplos irei demonstrar este assunto de forma simples, mas funcional.
Obter o nome da função Dinamicamente
Quando precisarmos ou necessitarmos de saber dinamicamente o nome da função dentro da mesma, podemos usar a valor guardado dentro do array “FUNCNAME”, eu vou usar nos exemplos para facilitar a explicação. Não é algo que seja muito usado no entanto, aqui fica.
echo ${FUNCNAME[0]}
Leia mais sobre os arrays em BASH aqui.
Retornar valores
Como disse inicialmente neste artigo, que o objetivo das funções em BASH é o mesmo do em qualquer outra linguagem é verdadeiro.
No entanto existem linguagens que retornam tipos de dados. Valores formatados de certa forma inteiros/objectos/strings/etc.
No caso do BASH, que não lida com tipos de dados por defeito, funciona como qualquer outro comando, retornando o texto que foi produzido na função bem como o valor de retorno 0-255 que é capturado pela variável especial “$?”. Leia mais sobre as variáveis especiais do BASH.
Este valor pode ser modificado por nós dentro da função usando o comando interno “return”.
Se não usarmos o comando “return” para retornar um valor desejado por nós, será retornado “0” como valor por defeito.
Para além do valor de retorno de execução também retorna o output gerado na função. Quando usamos echo/printf ou outro comando que gere algum output.
Nos exemplos veremos como capturar este output, que em muitos casos é necessário para scripts mais elaborados.
nome(){
comando1
comando2
comandoN
return 5
}
Normalmente os comandos do sistema retornam “0” quando correu bem a execução do comando e de “1” a “255” para identificar um problema na execução do mesmo. É este o principio usado em shell em todos os sistemas que conheço. No entanto, fica à decisão de cada um usar outro tipo de lógica se assim for necessário.
O comando “return” também retorna de imediato da função, não executa qualquer código que a função contenha depois do comando “return”.
nome(){
# retorna o valor 10
return 10
# Estes comandos já não são executados
comando1
comando2
comandoN
return 5
}
Neste caso a função retornava o valor 10 e ignora todo o outro código.
Escopo das Variáveis
O escopo de variáveis define como as variáveis são acedidas por outras partes do código.
- Variáveis de ambiente – Podem ser acedidas por todo o código, e criadas dentro de funções. Podemos criar uma variável dentro de uma função e ter acesso a esta variável fora desta função.
- Variáveis globais – Podem ser acedidas por todo o código do script e podem ser criadas dentro das funções. Podemos criar uma variável dentro de uma função e ter acesso a esta variável fora desta função.
- Variáveis locais – Só podem ser definidas dentro das funções e acedidas dentro da função que as criou.
Para criar uma variável local, que só queremos usar dentro da função para conter dados temporários enquanto a função é executada. Usamos a comando interno “local” antes da definição da variável. Leia aqui sobre variáveis.
nome(){
# Definir uma variável local, que pertence ao escopo da função
local a='um texto qualquer como valor'
# realizar algum trabalho com a variável
}
Esta é a forma correta de criar variáveis dentro das funções, quando queremos que elas apenas sejam acedidas dentro da função.
Se não usarmos o comando interno “local”, para definir a variável é definida uma variável global.
Se pretendermos criar uma variável de ambiente dentro de uma função, usamos o comando interno “export” antes da definição da variável.
nome(){
# Definir uma variável local, que pertence ao escopo da função
export a='um texto qualquer como valor'
# realizar algum trabalho com a variável ou não, pode ser para ser usada depois fora da função
}
Nos exemplos, iremos constatar o seu funcionamento que embora simples mas importante.
Exemplos
Nestes exemplos irei tentar cobrir todo o artigo, com exemplos fáceis, mas funcionais de tudo o que até aqui foi visto. Desta forma podem ficar com algum material funcional para testar, modificar, perceber.
Declarar Funções
Declarando funções de todas as maneira possíveis.
Apenas neste exemplo, pois por questões práticas nos outros exemplos irei usar a forma mais legível e intuitiva. No entanto a forma deve ser decidida para o script que estamos a desenvolver.
#!/bin/bash
#Definição de funções
# Múltiplas linha
# Declarar função usando a palavra reservada "function"
function func1 () {
echo Escrito dentro da função func1
}
# Declarar função sem usar a palavra reservada "function"
func2 () {
echo Escrito dentro da função func2
}
# Em uma única linha
# Declarar função usando a palavra reservada "function"
function func3() { echo Escrito dentro da função func3; }
# Declarar função sem usar a palavra reservada "function"
func4() { echo Escrito dentro da função func4; }
# Chamar as nossas funções
func1
func2
func3
func4
Quando corremos o script “functions_ex_1.sh” temos o seguinte resultado:
miguel@terra:~/bash/functions$ ./functions_ex_1.sh
Escrito dentro da função func1
Escrito dentro da função func2
Escrito dentro da função func3
Escrito dentro da função func4
miguel@terra:~/bash/functions$
Passagem de argumentos
Neste exemplo, podemos ver como conseguimos ter acesso aos argumentos passados à nossa função:
#!/bin/bash
# Passagem de argumentos
# Múltiplas linha
# Declarar função usando a palavra reservada "function"
function recebe_argumentos () {
echo 'Dentro da função "recebe_argumentos"'
# Quantos argumentos foram passados para a função
echo Foram passados $# argumentos
# Todos os argumentos passados à função
echo Todos os argumentos passados: "$*"
echo Todos os argumentos passados: "$@"
# Argumentos
echo Argumento1: $1
echo Argumento2: $2
echo Argumento3: $3
echo Argumento4: $4
}
# Chamar a função com quatro argumentos
recebe_argumentos arg1 arg2 arg3 "arg4 mais"
Ao corrermos o script “functions_ex_2.sh” temos o seguinte resultado:
miguel@terra:~/bash/functions$ ./functions_ex_2.sh
Dentro da função "recebe_argumentos"
Foram passados 4 argumentos
Todos os argumentos passados: arg1 arg2 arg3 arg4 mais
Todos os argumentos passados: arg1 arg2 arg3 arg4 mais
Argumento1: arg1
Argumento2: arg2
Argumento3: arg3
Argumento4: arg4 mais
miguel@terra:~/bash/functions$
Retornar valores
Como vimos anteriormente, sabemos que podemos retornar valores de execução, a fim de saber como correu a execução da função. Neste exemplo irei demonstrar todos os conceitos tratados neste artigo em relação ao retorno de funções.
#!/bin/bash
# Retornar valores
# Múltiplas linha retorna1
function retorna1 () {
# Criar uma variável pois a função não pode estar vazia
a=0
}
# Chamar a função retorna1 e guardar o output em uma variável
output=$(retorna1)
# escrever o valor de retorno, que será zero pois não retornamos outro valor
echo O valor de retorno é: $?
# Escrever o output gerado na Função, será vazio pois nada foi escrito
echo O output da função é: $output
# Escrever linha em branco para separar o output das funções
echo
# Múltiplas linha retorna2
function retorna2 () {
# Escrever algo para o output
echo Texto escrito para o output na função: ${FUNCNAME[0]}
# Retornar o valor definido por nós
return 10
}
# Chamar a função retorna2 e guardar o output em uma variável
output=$(retorna2)
# escrever o valor de retorno, que será 10 pois usamos return 10
echo O valor de retorno é: $?
# Escrever o output gerado na Função
echo O output da função é: $output
# Escrever linha em branco para separar o output das funções
echo
# Múltiplas linha retorna3
function retorna3 () {
# Escrever algo para o output
echo Texto escrito para o output na função: ${FUNCNAME[0]}
# Retornar o valor definido por nós
return 20
# Todo o código seguinte será ignorado, não será executado
# pois temos o return 20 antes deste comentário
echo Este código será ignorado
echo Este código será ignorado
echo Este código será ignorado
echo Este código será ignorado
echo Este código será ignorado
echo Este código será ignorado
}
# Chamar a função retorna3 e guardar o output em uma variável
output=$(retorna3)
# escrever o valor de retorno, que será 10 pois usamos return 10
echo O valor de retorno é: $?
# Escrever o output gerado na Função
echo O output da função é: $output
Ao corrermos o script “functions_ex_3.sh” temos o seguinte resultado:
miguel@terra:~/bash/functions$ ./functions_ex_3.sh
O valor de retorno é: 0
O output da função é:
O valor de retorno é: 10
O output da função é: Texto escrito para o output na função: retorna2
O valor de retorno é: 20
O output da função é: Texto escrito para o output na função: retorna3
miguel@terra:~/bash/functions$
Neste exemplo, podemos perceber com obter o valor de retorno bem como o output gerado nas funções. Claro que usamos um ou os dois dependendo das necessidades dos scripts que desenvolvemos.
Escopo das Variáveis
Neste exemplo, pretendo demonstrar o escopo das variaveis:
#!/bin/bash
# Escopo de variáveis
# Múltiplas linha
# Declarar função usando a palavra reservada "function"
function escopo_variables () {
# Criar a variável de ambiente será acedida dentro e fora da função
export variavel_de_ambiente='Valor na variável de ambiente'
# Criar a variável global será acedida dentro e fora da função
variavel_global='Valor na variável global'
# Criar a variável local será acedida apenas dentro da função
local variavel_local='Valor na variável local'
#Depois de criar as variáveis vamos escrever os seus valores
echo Estado da variável de ambiente: $variavel_de_ambiente
echo Estado da variável global: $variavel_global
echo Estado da variável local na função: $variavel_local
}
echo Antes de chamar a função os valores lidos são:
# Antes de correr a função vamos escrever o valor das variáveis
# a fim de testar os seus valores antes de chamarmos a nossa função
echo Estado da variável de ambiente: $variavel_de_ambiente
echo Estado da variável global: $variavel_global
echo Estado da variável local na função: $variavel_local
# Escrever uma linha vazia
echo
echo Dentro da função os valores são:
# Chamar a função escopo_variables
escopo_variables
# Escrever uma linha vazia
echo
echo Depois de chamar a função os valores são:
# Depois de correr a função vamos escrever o valor das variáveis
# a fim de testar os seus valores depois de chamarmos a nossa função
# que cria as variáveis
# O valor da variavel de ambiente será acedido pois temos acesso de todo o lado no script
echo Estado da variável de ambiente: $variavel_de_ambiente
# O valor da variavel global será acedido pois temos acesso em todo o lado no script
echo Estado da variável global: $variavel_global
# Esta variavel que foi criada na função como local, não pode ser acedida de fora da função
echo Estado da variável local na função: $variavel_local
Ao corrermos o script “functions_ex_4.sh” temos o seguinte resultado:
miguel@terra:~/bash/functions$ ./functions_ex_4.sh
Antes de chamar a função os valores lidos são:
Estado da variável de ambiente:
Estado da variável global:
Estado da variável local na função:
Dentro da função os valores são:
Estado da variável de ambiente: Valor na variável de ambiente
Estado da variável global: Valor na variável global
Estado da variável local na função: Valor na variável local
Depois de chamar a função os valores são:
Estado da variável de ambiente: Valor na variável de ambiente
Estado da variável global: Valor na variável global
Estado da variável local na função:
miguel@terra:~/bash/functions$
Como podemos ver no resultado do comando, antes de chamarmos a função todas as variáveis não têm valor. Pois não existem ainda.
Depois chamamos a função, as variáveis são criadas e são escritas os seus valores. E como podemos ver todas as variavas a de ambiente e a global e a local, todas têm o valor esperado.
Depois de chamar a função, já fora da função os valores das variáveis são escritos novamente. Como podemos constatar, a variável “$variavel_local” que foi criada como local dentro da função não tem valor, pois esta variável não existe fora do escopo de aquela função.
Conclusão
Com as funções em BASH podemos definir código para realizar uma determinada tarefa bem definida. Podemos chamar a mesma função no decorrer dos scripts varias vezes para realizar o seu trabalho e mantendo o condigo organizado.
A ideia destes artigos é ser bem simples e focar em apenas um ponto do BASH de cada vez. Nem sempre é possivel. Espero que tenha conseguido a simplicidade, para que qualquer um passa ler e tirar proveito destas informações.