Shell - Funções

O artigo tem por objetivo, introduzir o tão importante e poderoso conceito de funções. Ele mostra: como declarar (no padrão de escrita das funções), funções aninhadas, arquivos de funções, undeclaring (desdeclarando ?), passando argumentos, recebendo dados de retorno, recursão, etc.

[ Hits: 37.378 ]

Por: jarlisson moreira em 27/07/2012


Para entender recursão, tem que entender recursão



Ao fim desta parte do artigo, espero que consiga entender essa famosa frase sobre recursão.

Basicamente, usamos recursão quando, dentro de uma função, chamamos ela mesma. Porém, alteramos o argumento passado a cada chamada.

Assim, o processo executa até um determinado ponto. Você vai confundir-se se eu falar muito antes de mostrar.

Então vamos a um exemplo, que foi usado pela banda Europe para criar sua música de maior sucesso: recursao.sh

#!/bin/bash

contagem_regressiva ()
{
  if [ $1 -eq 0 ]
  then
     clear
     echo "The Final Countdown !!!"
     sleep 1
     echo "tam-dam-dam-daaaam"
     sleep 1
     echo "tam-nam-nam-nam-naaaam"
     return 0
  fi

  echo $1
  sleep 1
  clear
  novo_tempo=`expr $1 - 1`
  contagem_regressiva $novo_tempo
}

clear
contagem_regressiva 5


Permita e execute:

chmod +x recursao.sh
$ ./recursao.sh


Vamos lá:
  • Primeiramente, clear limpa a tela do terminal e sleep 1 faz o terminal dormir por 1 segundo.
  • A função recebe um número como argumento, o número 5.
  • O primeiro 'if' teste se o argumento é 0, se for mostra a letra da música na tela. Como não é, o 'if' não é executado.
  • Depois, o argumento é impresso na tela. Então é criada uma nova variável a partir do argumento subtraído de 1.
  • Depois vem o pulo do gato, que é chamar a própria função, mas agora usando o 4 como argumento.
  • Esse processo é repetido até o argumento ser 0. Quando é 0, a função vai, finalmente, rodar o 'if'.
  • Note que o 'if' tem o 'return' que irá parar a função. E só para quando atinge 0 e mostra a letra da música do Europe.

Ou seja, os dois pontos obrigatórios da recursão: onde parar (no caso, quando for '0') e chamar a própria função, porém, com argumento alterado em relação a aquele que foi recebido.

Recursão é algo de extrema utilidade, pois trata casos de forma dinâmica. Ou seja, não importa o argumento, se for positivo, vai sempre executar a contagem regressiva. Recursão é alto de extrema de importância e uma maneira muito bonita de se resolver problemas.

No geral, exibe que bem menos código seja escrito, embora, no começo, pareça ser mais complicado.

Como exercício, sugiro que você crie uma função que calcule o fatorial de um número positivo.

Exemplo:
  • 3! = 3x2x1= 6
  • 4! = 4x3x2x1 = 24
  • 5! = 5x4x3x2x1 = 120

* Dica: Fatorial de 'n' pode ser visto como 'n x fatorial(n-1)', e fatorial de 1 é 1.

Artigo produzido para o Viva o Linux e para o blog Programação Progressiva.

Página anterior    

Páginas do artigo
   1. Introduzindo e declarando funções
   2. Funções - Argumentos - Retorno
   3. Para entender recursão, tem que entender recursão
Outros artigos deste autor

AWK - Introdução

Sed - Introdução

Pipelines (Canalizadores)

Processos

LibreOffice Math

Leitura recomendada

Backup automático em Shell Script

O que é Shell Script

Debugando shell scripts

Como matar um processo - kill, killall, pkill e xkill

Como trabalhar com funções em bash

  
Comentários
[1] Comentário enviado por removido em 27/07/2012 - 13:16h

Um detalhe:

Quando um programa qualquer retorna um valor para o sistema, o valor retorna em módulo 256.

Por exemplo:

Para "return 1000", o valor do $? aparecerá como 232, porque 1000 dividido por 256 sobram 232.

Isso acontece com outras linguagens. Considera-se apenas o valor de um byte sem sinal.

[2] Comentário enviado por removido em 27/07/2012 - 17:07h

Boa colaboração Jarlisson!

Mas tenho que fazer uma observação e peço que me corrija se estiver errado.

Você falou: Como vimos, todo e qualquer comando do Unix retorna algum código. blz ! mas nem sempre os códigos de retorno que vimos na tela vendo o conteúdo da variável $? são retornados por comandos e muitos desses códigos retornados são feitos pelo shell.

POR EXEMPLO: o código: 126 indica que o comando não tem permissão de execução. outro exemplo é quando executa um comando que não existe, quem retorna o erro é o próprio shell. pois o mesmo não encontrou aquela função embutida no shell usado e não encontrou no sistema de arquivos que contém o S.O.

abraço amigo.

[3] Comentário enviado por rai3mb em 27/07/2012 - 17:56h


[1] Comentário enviado por Listeiro 037 em 27/07/2012 - 13:16h:

Um detalhe:

Quando um programa qualquer retorna um valor para o sistema, o valor retorna em módulo 256.

Por exemplo:

Para "return 1000", o valor do $? aparecerá como 232, porque 1000 dividido por 256 sobram 232.

Isso acontece com outras linguagens. Considera-se apenas o valor de um byte sem sinal.


Legal, entendi porque algumas aplicações usam seu código de retorno até o 255, pois quando for superior o retorno poderá confundir, já que será o módulo de 256 ;-)

[4] Comentário enviado por removido em 27/07/2012 - 18:09h


[2] Comentário enviado por eabreu em 27/07/2012 - 17:07h:

Boa colaboração Jarlisson!

Mas tenho que fazer uma observação e peço que me corrija se estiver errado.

Você falou: Como vimos, todo e qualquer comando do Unix retorna algum código. blz ! mas nem sempre os códigos de retorno que vimos na tela vendo o conteúdo da variável $? são retornados por comandos e muitos desses códigos retornados são feitos pelo shell.

POR EXEMPLO: o código: 126 indica que o comando não tem permissão de execução. outro exemplo é quando executa um comando que não existe, quem retorna o erro é o próprio shell. pois o mesmo não encontrou aquela função embutida no shell usado e não encontrou no sistema de arquivos que contém o S.O.

abraço amigo.


Isto, a rigor realmente acontece. Mas há, como em tudo em Unix, a flexibilidade de enviar um número qualquer como retorno.

É só tomar cuidado com certos números e fazer a verificação caso a caso de qual situação aplica-se o que foi retornado.

Nisto, 0 é usado sempre quando tudo correu bem e 255 (255 == -1 módulo 256) costuma identificar sempre algum erro.

*** ADD ***

No caso do 126, se for resultante este código antes de algum "return", é analisada qual a situação e depois enviado um "return com um número dos seus".


[5] Comentário enviado por removido em 27/07/2012 - 18:12h


[3] Comentário enviado por rai3mb em 27/07/2012 - 17:56h:


[1] Comentário enviado por Listeiro 037 em 27/07/2012 - 13:16h:

Um detalhe:

Quando um programa qualquer retorna um valor para o sistema, o valor retorna em módulo 256.

Por exemplo:

Para "return 1000", o valor do $? aparecerá como 232, porque 1000 dividido por 256 sobram 232.

Isso acontece com outras linguagens. Considera-se apenas o valor de um byte sem sinal.

Legal, entendi porque algumas aplicações usam seu código de retorno até o 255, pois quando for superior o retorno poderá confundir, já que será o módulo de 256 ;-)


Isso ocorria/ocorre no DOS também. Lá chamavam de ERRORLEVEL.

[6] Comentário enviado por jarlisson em 27/07/2012 - 20:32h

Esses códigos altos de retorno são mais raros...por exemplo:
128 + x: fatal error signal “x” (137, pro caso do x=9, que é o do kill -9)
130 - ctrol+c
Mas acho que o erro mais raro que ouvi falar foi o:
2: misuse of shell built-in

Como o eabreu falou, algumas coisas são do próprio shell. Aí vale uma ressalva...sempre que eu ia me informar melhor sobre algumas coisas eu via que em determinado Shell podia, em outro não...outras coisas que podiam, mas não eram muito usadas.

Na verdade achei centenas de detalhes, principalmente pelo número diferente de shells: C shell, korn shell, t c shell, z shell, ash...embora alguns já estejam mortos, muitos comandos o Bash herdou totalmente, outros comandos foram herdados em certas condições e outros não foram herdados, mas ainda há gente usando todo o tipo de shell, produzindo material, livros etc.

Esses códigos de retorno são mais usados pra checar se ocorreu tudo bem ou não. Mas eu vi muita discussão a respeito desse intervalo 0-255, pois alguns alguns comandos/programas não se deram satisfeito com isso.
A saída que vi foi, caso o erro fosse maior, eles iam 'tratando'... 'agrupando' em um número...por isso é possível ter vários problemas com o mesmo código de retorno.

Quando a coisa era mais elaborada, eles passavam argumentos pra outras funções que explicavam melhor o erro, antes de agrupar esses erros e retornar o código:

descricao ()
{
if [ $1 -ge 2 ]; then echo "erro tal"
elif [ $1 -eq 1 ]; then echo "voce esqueceu isso"
elif [ $1 -eq 0 ]; then echo "ocorreu tudo bem"
fi
}
echo "ocorreu o problema $(descricao $numero)"


Mas enfim, vi muita flexibilidade...sempre achava que algo nao podia, vi que podia, mas de uma outra forma ou em outro shell ou através de mudanças no bash. Eu não me aprofundei muito nos detalhes, pois eram muitos e muita coisa específica, e o objetivo do artigo era ser bem introdutório mesmo.

Mas qualquer limitação que voces acharem que possam ter no shell, deem uma pesquisada, ṕois sempre vai dar pra contornar.

[7] Comentário enviado por rhuicamelo em 28/07/2012 - 11:36h

artigo de shell é mais pra referência...até a documentação é bizarra
shell é bizarro

sempre que pensar nao da pra fazer algo no shell, pesquise que vera que da pa fazer. as vezes pode mas é em outro shel ou com plugin ou baixando mais comandos

daqui a pouco shel vai ser orientado a objetos

[8] Comentário enviado por di4s em 30/07/2012 - 00:38h

Otimo artigo.

uma duvida que tenho é como retornar algo que a função fez ( entenda usar o return como nas outras linguagens ), algo do tipo:

variavel = funcao(); #função está retornando um valor que está sendo armazenado em variavel

atualmente faço assim:

#!/bin/bash

function quadrado() {

echo $(($1*$1));
}

resultado=$(quadrado 5);



alguém sabe de outra forma?






[9] Comentário enviado por removido em 30/07/2012 - 02:45h


[8] Comentário enviado por prmjunior em 30/07/2012 - 00:38h:

Otimo artigo.

uma duvida que tenho é como retornar algo que a função fez ( entenda usar o return como nas outras linguagens ), algo do tipo:

variavel = funcao(); #função está retornando um valor que está sendo armazenado em variavel

atualmente faço assim:

#!/bin/bash

function quadrado() {

echo $(($1*$1));
}

resultado=$(quadrado 5);



alguém sabe de outra forma?







Vou adiantar um pedaço de um texto :-P :-P :-P :-P

[code]#!/bin/bash

export variavel


func1(){

let a=2+2;

variavel=$a;

return;

}

variavel=2;

echo "Antes: $variavel"

func1;

echo "Depois: $variavel"[/code]


Faça o teste para ver se isto serve.

Dependendo do que você disser eu postarei o resto mais rápido :-P :-P :-P :-P

*** ADD ***

O <CODE> não funcionou...

[10] Comentário enviado por jarlisson em 30/07/2012 - 06:07h

Como as variaveis internas sao globais, eu criaria uma la dentro que ficaria mudando conforme o argumento:

-----------------
#!/bin/bash

dobra(){
res=`expr 2 \* $1`
return
}

dobra 2
dobro2=$res
echo "o dobro de 2 é $dobro2"

dobra 3
dobro3=$res
echo "o dobro de 3 é $dobro3
------------------

Ou seja, variavel 'res' seria meu return.
Mas deve haver outros métodos...

[11] Comentário enviado por jarlisson em 30/07/2012 - 06:39h

Deixar essa URL aqui como referência, achei muito boa:
http://www.linuxtopia.org/online_books/advanced_bash_scripting_guide/complexfunct.html

[12] Comentário enviado por removido em 05/08/2012 - 22:31h


[9] Comentário enviado por Listeiro 037 em 30/07/2012 - 02:45h:


[8] Comentário enviado por prmjunior em 30/07/2012 - 00:38h:

Otimo artigo.

uma duvida que tenho é como retornar algo que a função fez ( entenda usar o return como nas outras linguagens ), algo do tipo:

variavel = funcao(); #função está retornando um valor que está sendo armazenado em variavel

atualmente faço assim:

#!/bin/bash

function quadrado() {

echo $(($1*$1));
}

resultado=$(quadrado 5);



alguém sabe de outra forma?







Vou adiantar um pedaço de um texto :-P :-P :-P :-P

[code]#!/bin/bash

export variavel


func1(){

let a=2+2;

variavel=$a;

return;

}

variavel=2;

echo "Antes: $variavel"

func1;

echo "Depois: $variavel"[/code]

Faça o teste para ver se isto serve.

Dependendo do que você disser eu postarei o resto mais rápido :-P :-P :-P :-P

*** ADD ***

O <CODE> não funcionou...




Então, respondendo conforme tinha me perguntado.

Comecei a escrever meia-dúzia de coisas sobre shell e parei por não saber em que tipo de temática elas se enquadrariam.

Modos:

1. variável global: foi isso aí de cima que mostrei e que "lembra" alguma coisa que fica "registrada" (tipo 'registrador' mesmo) para acesso imediato.

2. uso de arquivo temporário. essa não fui eu quem inventou e é bem conhecida. usa-se 'echo > variavel.$$', algo assim, por exemplo. o resto depende do que for para o arquivo.

3. um array global em que cada posição funciona como uma memória individual e usa-se o valor do return para o índice. a vantagem é que os valores retornados ficam armazenados ordenadamente e para uso posterior.

4. o modo clássico do `echo`, que é o mais usado.


Ainda não sei se há um modo de fazer isso com shift, que é para entrada de parâmetros e não saída.

O uso de shell script costuma ser para tarefas mais simples. Não é o tipo de coisa que usam para construir bancos de dados de texto.

É melhor não ficar filosofando sobre como pode ser usado. Não é adequado. Exceto neste minuto.


Teve um cara que escreveu um assembler em shell, mas não tenho a menor ideia de como fez. Não é difícil de encontrar no GOOGLE.

[13] Comentário enviado por Novaesma em 04/09/2020 - 11:39h

Então ficaria assim, assim a função e posso ta chamando essa função em outra parte do script??

CHPG ()
{
echo $ scp /home/saai/Catalogo/CHPG/* ---@ip:/home/---/Catalogo/CHPG/
}


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts