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.392 ]

Por: jarlisson moreira em 27/07/2012


Funções - Argumentos - Retorno



Funções aninhadas

Você também pode chamar uma função dentro de outra função. Neste caso, a ordem em que as funções foram declaradas não é importante.

Ou seja, você pode invocar uma função que ainda não foi declarada.

Porém, por questão de organização e manutenção de código, declare sempre suas funções no início do script.

Arquivos de função

Se o seu script começar a ficar muito grande e complexo, é uma boa prática criar um arquivo (ou mais), só pra armazenar as funções.

Então, no script, simplesmente carregue esse arquivo (como o include, import, use, etc - de outras linguagens).

Para carregar as funções de um arquivo, caso este esteja no mesmo diretório do script, use source:

source arquivo_de_funcoes


Desdeclarando funções

Nem sei se a palavra "desdeclarar" existe, mas foi o que imaginei de Undeclaring functions.

Isso é feito através do comando unset:

unset listar

Se tentarmos o comando 'listar', teremos mensagem de erro, pois o shell já a 'liberou' de sua 'memória'.

Passando argumentos para as funções

Como, depois de declaradas, podemos usar as funções assim como usamos os comandos, também podemos passar argumentos para funções, assim como fazemos com os comandos.

Os argumentos são armazenados da mesma forma que os parâmetros, quando usamos comandos. Ou seja, o nome do script é armazenado na variável '$0', o 1º argumento fica na variável '$1', o 2º na '$2', ... O número total de argumentos na '$#' e todos são armazenados na '$@'.

Vamos testar criando o script "args.sh":

#!/bin/sh

args()
{
       echo "Nome: $0"
       echo "Total: $#"
       echo "Primeiro: $1"
       echo "Segundo: $2"
       echo "Terceiro: $3"
       echo
       echo "Todos: $@"
       echo
}

args arg1 arg2 arg3


Permita e execute:

chmod +x args.sh
$ ./args.sh


Códigos de retorno (return codes)

Como vimos, todo e qualquer comando do Unix retorna algum código. '0' caso tenha dado tudo certo e outro número para mostrar que houve um erro, onde números diferentes são usados para indicar erros diferentes.

O bash armazena o código de retorno do último comando na variável '$?'.

Por exemplo, digite no terminal:

comando_inexistente
$ echo $?


Que exibirá o erro, pro caso do comando não existir.

Agora, teste um comando que funcione bem:

ls
$ echo $?


Return

Como estes códigos de saída são amplamente usados no *Unix, é uma boa prática fazer o mesmo em seus scripts. Usa-se o comando return [|inteiro], ao invés de exit [|inteiro] nas funções de seu script, pois queremos somente que a função termine, não o script, e retornando um inteiro para o script, fazendo uma comunicação deste com a função.

Se não usarmos um return em uma função, o '$?' armazenará o código de retorno do último comando da função.

Podemos escrever só 'return' sem retornarmos explicitamente um número. Quando a função encontra 'return' ela termina imediatamente (não executa o que vem em seguida) e '$?' armazenará o código de retorno do último comando antes de encontrar da função chegar em 'return'.

Ou retornamos algo específico de nosso propósito. Dentro de condicionais, por exemplo:

if(tudo certo)
       return 2112
else
       return 666


Variáveis globais

* Aqui vai uma pequena e importante ressalva, para você que já vem de outra linguagem de programação:

Por padrão, as variáveis em Shell são globais. Se desejar ter uma variável local em suas funções, as declare como 'local'. Exemplo:

local VAR='Existem variáveis locais!


Sem isso, as variáveis que você cria dentro das função estarão disponíveis por todo o script, ou caso já existam serão sobrescritas. Portanto, cuidado, dê nomes únicos para as suas variáveis ou adicione o local na frente.

Página anterior     Próxima página

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

LibreOffice Math

Pipelines (Canalizadores)

Sed - Introdução

Processos

AWK - Introdução

Leitura recomendada

expr - Definição e uso do comando

Introdução a LUA, uma poderosa linguagem de programação

AWK - Manipulação de arquivos de texto

Programação com Shell Script

Trabalhar com array no 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