Brincando com vetores

Não, não vou falar de como combater o mosquito da dengue. Vou falar um pouco de vetores, que até há algum tempo atrás eu nem sabia que existiam em shell. Espero que vocês fiquem tão impressionados quanto fiquei!

[ Hits: 70.632 ]

Por: Leandro Santiago em 23/01/2007 | Blog: http://leandrosan.wordpress.com


Vetores como argumento de uma função



Até agora está tudo bem. Mas eu acabei me deparando com uns probleminhas como: Como passar um vetor como argumento para uma função?

Como disse anteriormente, escrever $vetor é escrever o primeiro elemento (índice 0) de vetor. Ou seja, se passarmos o mesmo como $vetor, estaremos usando somente o primeiro elemento.

Ora, pois para passar um determinado vetor como parâmetro de uma função, é preciso "expandir" o seu conteúdo antes de executar a função, para depois passar os seus elementos como argumento. Um exemplo?

#!/bin/bash
## A função abaixo devolve a quantidade de números negativos, positivos e nulos que há num vetor
## Da forma "negativos:neutros:positivos"

function quant
{
   local vetor=($@)
   local negativo=0
   local positivo=0
   local neutro=0
   local Tamanho=${#vetor[@]}
   for ((i=0;i<Tamanho;i++))
   do
      if ((vetor[i]>0)); then ((positivo++))
      elif ((vetor[i]<0)); then ((negativo++))
      else ((neutro++)); fi
   done
   echo "$negativo:$neutro:$positivo"
   # Você pode usar o resultado acima para o que quiser, como com o cut, awk, ou sei lá...
}
vetor=(-1 -5 5 6 10 3 8 9 0)
vetor2=(-5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)
echo "Teste: $(quant ${vetor[@]}))"
quant ${vetor2[@]}

Deixa eu explicar a função acima:
  • quant() precisa de um argumento, que é um vetor. Mas o que são passados na verdade são os elementos deste. Cada um deles é reagrupado dentro de "vetor", já dentro da função. Aí é feito o processo de contagem, que é devolvido da forma "negativos:neutros:positivos".

Como é possível perceber, há um tremendo desperdício de processamento quando fazemos essa expansão e juntamos tudo de novo, mas esta foi a solução que eu arranjei. Se alguém aí souber de uma mais elegante ou mais eficiente, estou aceitando sugestões.

Nota: As variáveis vetor, negativo, positivo, neutro e Tamanho são criadas como locais para não terem problemas com as variáveis externas. Só por garantia mesmo ;).
Certo?

Se você executar o script acima, terá a seguinte saída:

Teste: 2:1:6
5:1:10

Outro exemplo:

Invertendo um vetor:

Primeiro escrevemos uma função que realiza a inversão dos elementos de um vetor:

Invert()
{
    A=($@)
    Tamanho=${#A[@]}
    for ((i=0;i<(Tamanho/2);i++))
    do
        x="${A[$i]}"
        A[$i]=${A[((Tamanho-i))]}
        A[((Tamanho-i))]="$x"
    done
    echo ${A[@]}
}

Inicializamos um vetor qualquer:

$ teste=(Zero Um Dois Três Quatro)

E usando a função acima:

$ Invert ${teste[@]}
Quatro Dois Três Um Zero

Nota: Esta saída é proveniente do echo que eu escrevi dentro da função, que é do vetor modificado dentro dela.

Eu não sei porque, mas as funções, do jeito que eu apliquei, não alteram o valor do elemento passado - Isso é o que normalmente chamamos de passagem de parâmetros por valor, ao contrário da passagem por referência, que modifica o valor da variável.

Para mostrar isso, exibimos novamente o conteúdo do vetor teste:

$ echo ${teste[@]}
Zero Um Dois Três Quatro

Mas há um modo um tanto tosco para contornar este problema, que é:

$ teste=(`Invert ${teste[@]}`)

Exibindo o conteúdo de teste:

$ echo ${teste[@]}
Quatro Dois Três Um Zero

Feito.

Strings como argumentos de funções:

Exemplo do uso dos índices de uma string numa função:

Função que recebe uma string e devolve quantas vezes um determinado caractere - Também recebido como parâmetro - aparece nela:

VezCarac()
{
    A="$1"
    Tamanho=${#A}
    cont=0
    for ((i=Tamanho-1;i>=0;i--))
    do
        [ "${A:$i:1}" = "$2" ] && ((cont++))
    done
    echo $cont
}

Executando:

$ VezCarac "Viva o Linux" i
2

Ou passamos como argumento uma variável que guarda uma string:

$ string="o rato roeu a roupa do rei de roma"
$ VezCarac "$string" r

5

Como foi possível ver, a função VezCarac() varre uma string, caractere por caractere, e se o este for igual ao passado, é incrementado um valor num contador.

Observação: Se você quiser inverter uma string, use o comando rev:

$ string=arara
$ echo $string | rev

arara

Bem, se não acreditou, tente esse outro:

$ echo linux | rev
xunil

Viva o Xunil!!!

Página anterior     Próxima página

Páginas do artigo
   1. Operações básicas com vetores
   2. Matemática e lógica com elementos de um vetor
   3. Vetores como argumento de uma função
   4. Considerações finais
Outros artigos deste autor

Instalando um ambiente leve e funcional em computadores antigos

Ogle: O player de DVD

Assistindo vídeos no XMMS

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

Alguns recursos do BASH para você utilizar em seus programas

Leitura recomendada

Hdparm - Entendendo seu funcionamento e criando um script para Slackware

Criando Arrays, Arrays Multidimensionais e Hashes em BASH Script

Colorindo o Shell

Como programar backup com rsync e cron de maneira rápida e simples

Recebendo seu IP dinâmico via email

  
Comentários
[1] Comentário enviado por tenchi em 23/01/2007 - 10:28h

Ae pessoal, eu esqueci de falar que, para que as operações funcionem corretamente, é preferível que os elementos do vetor não contenham o caractere espaço ' '.
Mas como isso pode ser feito? Basicamente substituindo o espaço por um caractere, como o underline (_).
Por exemplo, se os elementos de um vetor forem lidos pelo usuário:

(...)
indice=0
while <alguma coisa>
do
read elemento
vetor[$indice]=`echo $elemento | tr ' ' '_'`
let indice++
done
(...)

Assim, a string "O rato roeu a roupa" estará dentro do vetor da seguinte forma: "O_rato_roeu_a_roupa".
E quando formos ler o valor de um elemento, fazemos o seguinte:
echo ${vetor[$i]} | tr '_' ' '

Bem, espero que não se incomodem pela minha falta de atenção, pois sem esse negócio de substituir o espaço, um elemento "alguma coisa" logo viraria dois elementos: "alguma" e "coisa", o que seria um erro brutal.

E para passar um vetor por referencia, usamos o comando eval, que é muito útil.
Com ele, nos referirmos à um vetor não como ${vetor1[@]}, mas como vetor1 somente, o que acaba com aquele desperdício de processamento que eu comentei. Por exemplo, vou reescrever as funções Invert e quant que citei acima:


Invert()
{
eval 'Tamanho=${#'$1'[@]}'
eval 'for ((i=0;i<(Tamanho/2);i++))
do
x=${'$1'[$i]}
'$1'[$i]=${'$1'[((Tamanho-i))]}
'$1'[((Tamanho-i))]=$x
done'

}

function quant
{
local negativo=0
local positivo=0
local neutro=0
eval 'local Tamanho=${#'$1'[@]}'
eval 'for ((i=0;i<Tamanho;i++))
do
if (('$1'[i]>0)); then ((positivo++))
elif (('$1'[i]<0)); then ((negativo++))
else ((neutro++)); fi
done'
echo "$negativo:$neutro:$positivo"
}

Usando:
$ Nomes=(ana beto carlos daniel)
$ echo ${Nomes[@]}
ana beto carlos daniel
$ Invert Nomes
$ echo ${Nomes[@]}
daniel carlos beto ana

$ Numeros=( 5 6 10 -20 -60 0 0 0 5 30)
$ quant Numeros
2:3:5

Sei que assim as coisas ficam bem confusas, mas eu ainda estou procurando um jeito de melhorar isso tudo. E uma hora ou outra as coisas ficam confusas, não é?

Me desculpem mesmo pela falta de atenção.

Falow.

[2] Comentário enviado por dailson em 23/01/2007 - 13:01h

Rapaz....
Esse artigo deve entrar para a galera dos artigos mais úteis aqui do VOL
Parabéns cara!!!

[3] Comentário enviado por bryan em 23/01/2007 - 14:26h

Muito bom o artigo, gostei...
Deixa ainda mais completo o acervo de tutoriais sobre Shell Script aqui no VOL. =]

Bryan

[4] Comentário enviado por dailson em 23/01/2007 - 16:02h

Aproveitando o artigo de vetores, alguém sabe informar porque a variável $STRING no laço não funciona... ou melhor, ela é carregad, mas o sed não faz o que deveria fazer??

PALAVRAS=("google" "googleadservices" "atdmt")
TAMANHO_VETOR=`echo ${PALAVRAS[*]} | wc -w`
# Subtraio uma posicao do tamanho, pois o vetor começa na posicao 0 (zero)
let TAMANHO_VETOR--

# Limpeza
for i in `seq 0 $TAMANHO_VETOR`
do
STRING=`echo ${PALAVRAS[$i]}`
sed -n '/$STRING/!p' /etc/squid/hosts.txt > /etc/squid/hosts.txt.temp
mv /etc/squid/hosts.txt.temp /etc/squid/hosts.txt
done

[5] Comentário enviado por tenchi em 23/01/2007 - 16:55h

Hum... dailson, primeiramente, valew pelos comentários...
Segundo, acho que o seu sed não está funcionando pois o nome $STRING não está sendo substituido pelo conteúdo dele. Por causa das aspas simples...
Tente usar as aspas duplas..

Ou depure o seu script.
Faça o seguinte:
$ bash -xv script.sh

Assim é possível ver aonde está o erro.
Acredite, esse negócio de depurar programas é uma tremenda mão na roda.
Veja a saída do comando que vc vai saber o que cada coisa faz...

Falow.

[6] Comentário enviado por tenchi em 23/01/2007 - 16:57h

Ah, e valew à todos que elogiaram este artigo...
E leiam o primeiro comentário, pois ele esclaresce algumas falhas do artigo.

[7] Comentário enviado por linus black em 23/01/2007 - 17:38h

gostei mas eu estou com uma duvida cruel!!!
eu poderia por ex: criar anexando caminhos de icones as strings para automatizar a execução do script com entesão de criar um programa baseado nesta explanação. e como posso proceder.. 10 Já fiquei fãn deste cara...

[8] Comentário enviado por dailson em 24/01/2007 - 10:13h

Oi Tenchi

Na variável STRING não estou usando aspas e sim acento grave ` `
Mas o problema não estava ai e sim nas aspas do sed, quando retirei as aspas tudo funcionou normalmente!!!
Valeu a dica da depuração!!!!
E mais uma vez, parabéns!!

Dailson

[9] Comentário enviado por tenchi em 24/01/2007 - 21:43h

Então dailson, era dessas que eu tava falando! rss.
Foi mal, é que eu não sou muito bom nesse negócio de expressões regulares...
"Essas expressões regulares ainda vão me matar do coração"

Falow.

[10] Comentário enviado por tenchi em 29/05/2007 - 14:09h

Ah, e para quem gostou deste artigo, por favor leiam sua "continuação":
http://www.vivaolinux.com.br/artigos/verArtigo.php?codigo=6107

Mais uma vez, muito obrigado, e qualquer dúvida, basta me mandar um e-mail.

[11] Comentário enviado por vanervainer em 29/02/2008 - 17:40h

Execelente Artigo!!! Parabéns!!!

[12] Comentário enviado por stewe em 06/05/2013 - 19:53h

fantástico mano


parece fácil


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts