A organização de código em blocos reutilizáveis é uma prática fundamental no desenvolvimento de software, e no ambiente de shell scripting, as funções cumprem esse papel. Elas permitem encapsular sequências de comandos, que podem ser invocadas posteriormente pelo nome, funcionando como um alias para um conjunto de operações.
Existem diversas sintaxes para definir funções em shell scripts:
- Utilizando a palavra-chave
function:
function nome_da_funcao {
sequencia_de_comandos
}
- Usando parênteses após o nome da função:
nome_da_funcao() {
sequencia_de_comandos
}
- Uma variação da sintaxe com parênteses:
function nome_de_funcao () {
# Corpo da função
}
Ao trabalhar com funções, algumas regras devem ser observadas:
- Para chamar uma função dentro de outra, basta usar o nome da função chamadora.
- Se houver múltiplas definições para a mesma função, a última definida prevalecerá.
- Uma função deve ser definida antes de ser invocada. A ordem de definição de outras funções não importa, desde que a função a ser chamada já tenha sido previamente declarada.
Exemplo de definição e chamada de função:
minha_funcao () {
hostname
date
}
# Chamando a função
minha_funcao
Funções podem ser aninhadas:
funcao_interna () {
echo "Executando função interna."
}
funcao_externa () {
echo "Executando função externa."
funcao_interna
echo "Terminando função externa."
}
funcao_externa
Para listar todas as funções definidas em um script ou no ambiente atual, utiliza-se o comando declare -F. Para visualizar o conteúdo de uma função específica, use declare -f nome_da_funcao.
A exclusão de uma função é realizada com o comando unset nome_da_funcao.
Retorno de Funções
A instrução return encerra a execução de uma função e retorna um código de saída. Este código pode ser acessado posteriormente através da variável especial $?.
É crucial obter o valor de retorno imediatamente após a execução da função, pois $? armazena o código de saída do último comando executado. Os códigos de retorno devem estar no intervalo de 0 a 255; valores fora deste escopo serão tratados como o resto da divisão por 256.
Exemplo de retorno numérico:
#!/bin/bash
calcular_dobro () {
read -p "Digite um número: " numero
return $[numero * 2]
}
calcular_dobro
echo "Código de retorno: $?"
Para retornar valores maiores que 255 ou resultados de cálculos complexos, a abordagem mais comum é usar a saída padrão da função e capturá-la em uma variável:
#!/bin/bash
calcular_dobro () {
read -p "Digite um número: " numero
echo $[numero * 2]
}
resultado=$(calcular_dobro)
echo "Resultado calculado: $resultado"
O return também é usado para indicar sucesso ou falha em operações condicionais:
verificar_usuario () {
if [ "$USER" = "root" ]; then
echo "Este é o usuário administrador."
else
echo "Este não é o usuário administrador."
return 1 # Indica falha/não administrador
fi
}
Passagem de Parâmetros para Funções
Parâmetros podem ser passados para funções da mesma forma que para scripts, utilizando os argumentos posicionais $1, $2, etc.
Método 1: Capturando entrada do usuário:
somar_numeros () {
local soma=$[$1 + $2]
echo $soma
}
read -p "Digite o primeiro número: " num1
read -p "Digite o segundo número: " num2
resultado_soma=$(somar_numeros $num1 $num2)
echo "A soma é: $resultado_soma"
Método 2: Passando argumentos diretamente:
somar_numeros () {
local soma=$[$1 + $2]
echo $soma
}
resultado_soma=$(somar_numeros 10 20)
echo "A soma é: $resultado_soma"
Escopo de Variáveis em Funções
Variáveis declaradas dentro de uma função, por padrão, têm escopo global, ou seja, são acessíveis fora da função. Para restringir o escopo de uma variável ao ambiente da função, utilize a palavra-chave local.
Exemplo sem local:
#!/bin/bash
minha_funcao(){
i=8
echo "Dentro da função: $i"
}
i=9
minha_funcao
echo "Fora da função: $i"
Saída:
Dentro da função: 8
Fora da função: 8
Exemplo com local:
#!/bin/bash
minha_funcao(){
local i=8
echo "Dentro da função: $i"
}
i=9
minha_funcao
echo "Fora da função: $i"
Saída:
Dentro da função: 8
Fora da função: 9
Uso de Arquivos de Funções
Para melhor organização, funções comuns podem ser agrupadas em arquivos separados. Esses arquivos podem ser "fonteados" (executados no shell atual) usando o comando source ou o ponto (.) para que as funções definidas neles se tornem disponíveis no script principal.
Exemplo de um arquivo funcoes.sh:
#!/bin/bash
# Arquivo: funcoes.sh
identificar_os () {
if grep -i -q "CentOS Linux 7" /etc/os-release
then
echo "CentOS 7"
elif grep -i -q "Ubuntu" /etc/os-release
then
echo "Ubuntu"
else
echo "Sistema Operacional não identificado"
fi
}
definir_cores () {
VERMELHO="\033[0;31m"
VERDE="\033[0;32m"
NEGRITO="\033[1;37m"
RESET="\033[0m"
}
Em outro script, podemos usar essas funções:
#!/bin/bash
# Arquivo: principal.sh
source funcoes.sh # Ou . funcoes.sh
echo "Sistema Operacional: $(identificar_os)"
definir_cores
echo -e "${VERDE}Mensagem em verde.${RESET}"
Recursão de Funções
Funções podem chamar a si mesmas, um conceito conhecido como recursão. Um exemplo clássico é o cálculo de fatorial.
Exemplo de cálculo de fatorial usando loop for:
#!/bin/bash
resultado=1
read -p "Digite um número inteiro positivo: " num
for ((i=1; i<=num; i++)); do
resultado=$[$resultado * i]
done
echo "O fatorial de $num é $resultado."
Exemplo de cálculo de fatorial usando recursão:
#!/bin/bash
fatorial () {
if [ $1 -eq 0 ] || [ $1 -eq 1 ]; then
echo 1
else
echo $[$1 * $(fatorial $[$1 - 1])]
fi
}
read -p "Digite um número inteiro positivo: " numero_para_calcular
echo "O fatorial de $numero_para_calcular é $(fatorial $numero_para_calcular)."
Arrays em Shell Scripts
Arrays são estruturas de dados que permitem armazenar múltiplos valores sob um único nome de variável.
Definição e Acesso a Arrays
A sintaxe para definir arrays pode variar:
- Array com valores separados por espaço:
nome_array=(valor0 valor1 valor2) - Array com índices explícitos:
nome_array=([0]=valor0 [1]=valor1) - Criando a partir de uma string separada por espaços:
lista="valor0 valor1"``nome_array=($lista) - Definindo elementos individualmente:
nome_array[0]="valor1"``nome_array[1]="valor2"
Arrays em shell script podem conter tipos de dados mistos (numéricos e strings). É recomendável usar aspas ao definir elementos que contenham espaços ou caracteres especiais.
Exemplo de declaração e acesso:
meu_array=(10 20 30 40 50)
echo ${meu_array[0]} # Acessa o primeiro elemento (índice 0) -> 10
echo ${meu_array[*]} # Exibe todos os elementos separados por espaço -> 10 20 30 40 50
echo ${meu_array[@]} # Equivalente a [*] -> 10 20 30 40 50
echo ${#meu_array[@]} # Retorna o número de elementos no array -> 5
# Modificando um elemento
meu_array[1]=99
echo ${meu_array[@]} # Saída: 10 99 30 40 50
# Substituição de parte de um elemento (apenas na exibição, não modifica o array original)
array_frutas=(laranja banana melancia)
echo ${array_frutas[@]/melancia/uva} # Saída: laranja banana uva
echo ${array_frutas[@]} # Saída: laranja banana melancia
Ordenação de Arrays (Exemplo: Bubble Sort)
Um algoritmo comum para ordenação é o Bubble Sort. Ele funciona comparando pares de elementos adjacentes e trocando-os se estiverem na ordem errada, repetindo o processo até que o array esteja ordenado.
Implementação do Bubble Sort em Bash:
#!/bin/bash
numeros=(72 63 88 91 45)
tamanho=${#numeros[@]}
# Loop externo controla as passagens
for ((i=0; i<tamanho-1; i++)); do
# Loop interno compara elementos adjacentes
for ((j=0; j<tamanho-i-1; j++)); do
# Se o elemento atual for maior que o próximo
if [ ${numeros[j]} -gt ${numeros[j+1]} ]; then
# Realiza a troca
temp=${numeros[j]}
numeros[j]=${numeros[j+1]}
numeros[j+1]=$temp
fi
done
done
echo "Array ordenado: ${numeros[@]}"
Debug de Scripts com set -x
O comando set -x é uma ferramenta útil para depurar scripts shell. Ao ativá-lo, o shell exibirá cada comando antes de executá-lo, mostrando como as variáveis são expandidas e quais comandos estão sendo realmente chamados. Para desativar essa funcionalidade, use set +x.
Exemplo de uso:
#!/bin/bash
set -x # Ativa o modo de depuração
read -p "Digite sua nota (0-100): " GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]; then
echo "$GRADE - Excelente"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ]; then
echo "$GRADE - Bom"
else
echo "$GRADE - Insuficiente"
fi
set +x # Desativa o modo de depuração
Ao executar este script, você verá a expansão das variáveis e a execução dos comandos if/elif/else.