Brincando com vetores - complemento

Este é uma espécie de "complemento" do meu artigo anterior, sobre vetores. Neste eu apresento recursos como: utilizar a variável IFS com vetores, usar variáveis readonly, além de uma melhoria no método para passar um vetor como argumento para uma função. Espero que gostem.

[ Hits: 32.941 ]

Por: Leandro Santiago em 26/03/2007 | Blog: http://leandrosan.wordpress.com


Passando valores por referência em funções



Se você ler o meu artigo anterior, sobre vetores, verá que eu já falei mais ou menos o que vou falar agora.

Funciona assim:
Se quisermos passar um vetor (ou qualquer outro tipo de variável) para uma função, de modo que possamos modificar o valor do mesmo - ou de algum elemento dele - devemos informar não o conteúdo da variável ($var), e sim o seu nome (var).

Há basicamente dois tipos de passagem de argumentos para uma função: por valor e referência.
  • Por valor é, como diz o próprio nome, passarmos apenas o conteúdo de uma variável para a função.
  • Por referencia, passamos o nome de uma variável, e assim podemos fazer o que quisermos com ela.

Isso funciona bem na maioria das linguagens, onde normalmente há uma maior "personalização" nos nomes de variáveis.

Mas em shell script é diferente: Passamos as variáveis como sendo ${1},${2},...,${20}..., etc. Estes são os valores das variáveis 1,2,...20. Entendeu? Valores.

É um típico caso de passagem por valor. Para passarmos uma variável por referência, não podemos usar variáveis 1,2, etc., pois as variáveis devem sempre começar com um caractere alfabético, podendo ser seguido de um ou mais caracteres alfanuméricos. A questão é: Como resolver este problema?

Uma maneira que arrumei é a seguinte: Usar um comandinho muito legal, chamado eval. O eval também é um built-in do bash, que executa o conteúdo de uma string.

Como assim? Ah, não vou explicar. Se quiser saber, leiam este artigo:
Exemplo:

MostraConteudo()
{
   eval echo \$$1
}

O que essa função faz? Ela exibe o conteúdo de uma variável passada como parâmetro.

Vejamos um exemplo:

$ var_teste=35
$ MostraConteudo var_teste

35

Você poderia me dizer: "Ah, você está me enganando. Eu posso fazer uma função que faz a mesma coisa, mas menos complicada".

MostraConteudo1()
{
   echo ${1}
}

$ var_teste=35
$ MostraConteudo1 ${var_teste}

35

Aham... como podemos ver, as funções fazem a mesma coisa, mas MostraConteudo1 recebe somente o conteúdo de uma variável, tornando impossível sabermos, por exemplo, o seu nome, impossibilitando uma modificação na mesma.

Já na função MostraConteudo, eu passo como parâmetro, não o conteúdo, mas o nome de uma função. Dentro da função, é possível saber qual é o nome da variável (echo ${1}}, possibilitando uma futura modificação. E essa é a mágica da referência: modificar uma variável.

Se eu quisesse, por exemplo, atribuir o valor "50" à variável recebida como parâmetro, eu faria o seguinte:

$ AtribuiValor50()
{
   eval $1=50
}


Um exemplo simples:

$ teste2=10
$ AtribuiValor50 teste2
$ echo ${teste2}

50

Mas se passarmos um argumento por valor, torna-se impossível uma modificação dentro da função.

Tá, mas porque eu tô explicando esse negócio, se o assunto aqui são vetores?
Basicamente por um motivo: A única forma (que eu encontrei) de passarmos corretamente um vetor - seja para ser modificado ou não - é essa estranha maneira que eu utilizei.

Como assim?

No meu artigo anterior sobre vetores, eu apresentei uma maneira de passarmos um vetor como argumento de uma função, mas percebi que este método apresenta várias falhas, que não comentarei aqui, mas se você ler o artigo, provavelmente irá perceber.

O método consistia em expandir o conteúdo de um vetor, passando os elementos expandidos como se fossem vários parâmetros, que seriam aglomerados num outro vetor, já dentro da função.

Mas descobri um método mais eficiente, embora menos prático e trivial. Sim, e usando o eval!

Veja o exemplo:

$ MostraElemento()
{
   eval echo '${'$1'['$2']}'
}


Essa função recebe dois parâmetros que são: 1: um nome de um vetor. 2: um índice de um elemento desse vetor, elemento este que será impresso na tela.

Exemplo de uso:

$ vetorTeste=(Vinte "Vinte e um" "Vinte e dois" "Vinte e três" "Vinte e quatro")

Exibindo o elemento de índice 3:

$ MostraElemento vetorTeste 3
Vinte e três

Logicamente que a função apresentada é totalmente inútil, mas o exemplo foi somente didático.

Um exemplo onde eu modifico os valores dos elementos de um vetor: Os elementos de índice par são multiplicados por 2, e os de índice ímpar, por 3.

$ MultiplicaArray()
{
    eval local 'Tamanho=${#'$1'[@]}'
    for ((i=0;i<Tamanho;i++))
    do
        if [ $((i%2)) -eq 0 ]
        then
            eval '(('$1'[i]*=2))'
        else
            eval '(('$1'[i]*=3))'
        fi
    done
}


Funcionamento:

$ Array=(0 1 2 3 4 5 6)
$ echo ${Array[@]}
$ MultiplicaArray Array
$ echo ${Array[@]}

0 3 4 9 8 15 12

Façam os cálculos e verifiquem se está tudo certo ;-).

Sei que essa função ficou muito bizarra, mas as coisas ficam assim com o eval.

"Mas escrever funções assim é muito complicado!"

Realmente é. Acredite: Embora aqui elas já apareçam feitas, eu demoro um bom tempo para escrever cada uma.

Mas a dica que eu dou é a seguinte:
Primeiramente, escreva a função normalmente, com um nome qualquer no lugar dos nomes dos parâmetros. Logo em seguida, vá substituindo os lugares necessários pelo valor correto ($1, ${2}, etc), protegendo os lugares certos.

"Como assim protegendo?"
Proteger é fazer com que o bash não execute o conteúdo do comando.
Por exemplo, se quisermos, dentro da função, exibir o conteúdo de uma variável recebida como parâmetro, fazemos:

$ eval echo \$$1

Assim, primeiro o $1 será substituído pelo nome da variável - vamos supor que seja var - , e o outro cifrão entrará na frente do nome (ficando $var), sendo executado pelo eval, Isso será equivalente à:

$ echo $var

Há várias maneiras de protegermos comandos, que são: aspas simples (''), aspas duplas ("") e a contra-barra, ou barra inversa (\). Bem, você já deve saber disso, mas não custa lembrar. :)

Observação: Na função MultiplicaArray, eu uso um tipo de atribuição que muitos não conhecem, que é:

$ ((variavel=numero_inteiro))

Onde variavel é o nome da variável que irá receber o valor inteiro (-5,1,0,3,...) numero_inteiro, que pode também ser uma variável, ou um elemento de um vetor, ou uma expressão matemática, que utilize qualquer um dos tipos de variáveis citados.

Ela faz parte da parte aritmética do bash. Vai entender... rsrs.

Também é possível usar vários comandos ligados por pipes, com o comando eval. Exemplo: Função que ordena um Vetor (BubbleSort?,QuickSort?, BozoSort?):

$ OrdenaArray()
{
    eval $1'=(`echo ${'$1'[@]} | tr " " "\n" | sort -n | paste -s -d" "`)'
}


Basta proteger o pipe da execução do bash, como qualquer outro caractere especial, assim como disse várias vezes.. rsrs

Página anterior     Próxima página

Páginas do artigo
   1. A variável IFS
   2. Vetores read-only
   3. Passando valores por referência em funções
   4. Algumas funções que podem ser úteis
   5. Conclusão
Outros artigos deste autor

Assistindo vídeos no XMMS

Recursos avançados do bash que você não aprende na escola

Brincando com vetores

Instalando um ambiente leve e funcional em computadores antigos

Alguns recursos do BASH para você utilizar em seus programas

Leitura recomendada

Convertendo TXT ou HTML em OGG

Script de backup full + diferencial + compactador + restauração

gGedit como IDE para COBOL

Fazendo o bash contar piadas

Configurando OpenSSH no Windows Server 2003 para autenticação por chave (sem senha)

  
Comentários
[1] Comentário enviado por removido em 27/03/2007 - 17:07h

Muito bom meu caro tenchi ;)

[2] Comentário enviado por fernandoamador em 08/04/2007 - 23:55h

Ótimo artgo...


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts