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

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

Mudando o tema dos cursores do mouse no Linux

Rodando vídeos .rmvb no Linux

Assistindo vídeos no XMMS

Alguns recursos do BASH para você utilizar em seus programas

Leitura recomendada

Criando Arrays, Arrays Multidimensionais e Hashes em BASH Script

Hdparm - Entendendo seu funcionamento e criando um script para Slackware

Criando uma ISO bootável do OpenBSD

AWK - Manipulação de arquivos de texto

Kit de scripts para backup (Full + Diferencial + Samba + Rede)

  
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