Funções no Bash

Pergunta feita pelo Albano, do do grupo da comunidade debxp no telegram.


Funções no Bash

Pergunta

Albano pergunta:

A lógica de programação para implementação de funções, procedimentos ou derivações disso são iguais para o Shell como para C ou outras linguagens de programação? Ou existem diferenças além de sintaxe? Desculpem se minha pergunta foi irrelevante, mas realmente não consegui perceber essas diferenças...

Resposta

Sim, existem várias diferenças que já foram apontadas nos nossos cursos e vídeos:

tl;dr

  • No shell, uma função é qualquer comando composto que recebe um identificador:
nome() COMANDO_COMPOSTO
nome() {
    COMANDOS
}

Ou assim...

nome() { COMANDOS; }
  • No shell, uma função não possui a definição de parâmetros formais: ela está limitada a receber parâmetros posicionais, passados como argumentos na sua chamada...
:~$ nome() { echo "Olá, $1!"; }
:~$ nome Fulano
Olá, Fulano!
  • Como o shell não expressa valores, a instrução return (um comando, na verdade) não retorna nada e só serve para sair da função informando um estado de saída (sucesso ou erro)...
:~$ sub() { ((s=$1-$2)); return; }
:~$ sub 10 5; echo $s; echo $?
5
0
:~$ sub 5 5; echo $s; echo $?
0
1
:~$ soma() { local sum; sum=$(($1+$2)); }
:~$ soma 2 4; echo $sum

:~$

O que ainda não te contaram sobre funções no Bash

Uma coisa que quase ninguém conta e eu estava guardando para o curso Técnicas do Shell: nós podemos definir um redirecionamento junto com a definição da função. Na verdade, a sintaxe completa de uma função é:

nome() COMANDO_COMPOSTO [REDIRECIONAMENTOS]

Observe:

:~$ lista() { printf '%s\n' $1 $2 $3; } >> teste.txt
:~$ lista banana laranja morango
:~$ cat teste.txt 
banana
laranja
morango
:~$ lista abacate pitanga pera
:~$ cat teste.txt 
banana
laranja
morango
abacate
pitanga
pera

No exemplo, nós definimos a função incluindo um redirecionamento de append da saída para o arquivo teste.txt. Mas isso nos permite fazer coisas bem mais interessantes, como:

numerar() {
    count=$1
    while read line; do
        echo $count $line
        ((count++))
    done
} < $2

Nesta função, nós podemos informar o número inicial de uma contagem de linhas ($1) e o arquivo que contém as linhas que serão numeradas na saída padrão ($2):

~ $ numerar 10 teste.txt 
10 banana
11 laranja
12 morango
13 abacate
14 pitanga
15 pera

Ou seja, até o nome do arquivo participante do redirecionamento pode ser passado como argumento e ser expandido de um parâmetro posicional!

Outro exemplo de uso, é esta pequena implementação em Bash do utilitário paste:

paste_files () 
{ 
    mapfile FILE1;
    mapfile FILE2 < $2;
    for i in ${!FILE1[@]};
    do
        printf '%-12s %s\n' ${FILE1[i]} ${FILE2[i]};
    done
} < $1

Aqui, o nome do arquivo expandido em $1 será redirecionado para a entrada do comando interno mapfile, que lê todo um arquivo na entrada padrão e armazena suas linhas em um vetor (no caso, FILE1). Já o segundo mapfile, recebe o conteúdo do segundo arquivo pelo redirecionamento do nome expandido em $2, armazenando-o em FILE2.

Assim, a mesma função trabalha com dois redirecionamentos de leitura em pontos diferentes: um para um comando específico no corpo da função e outro para toda a função.

O resultado:

~ $ paste_files teste1.txt teste2.txt 
banana       abacate
laranja      pitanga
morango      pera

Músico, programador, designer e apaixonado pelos ideais do Software Livre.

1 Response

Deixe um comentário

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

Post comment