Linux slogan
Visite também: Segurança Linux · BR-Linux.org · Dicas-L · Doode · NoticiasLinux · SoftwareLivre.org · UnderLinux



» Screenshot
» Login
Login:
Senha:

Se você ainda não possui uma conta, clique aqui.

Esqueci minha senha



Artigo

Brincando com vetores
Linux user
tenchi
23/01/2007
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!
Por: Leandro Santiago | Blog: http://leandrosan.wordpress.com
[ Hits: 21053 ]
Conceito: 10.0   3 voto(s)3 voto(s)3 voto(s)3 voto(s)3 voto(s) + quero dar nota ao artigo

Operações básicas com vetores

Se há um recurso muito útil na programação, certamente é a possibilidade de agruparmos várias variáveis de um mesmo tipo em uma só. São o que chamamos de vetor, ou matriz - em inglês se diz array.

Há muito material na Internet que ensina a usar vetores, por isso não vou perder tempo com conceitos.

Neste artigo, eu pretendo apresentar para os que não conhecem alguns recursos úteis quando trabalhamos com vetores em shell script.

Aviso: Eu usarei o interpretador de comandos compatível com o sh, que é o bash. Todos os exemplos apresentados funcionam com certeza nele. Ou seja, eles não funcionarão em outros interpretadores não compatíveis com o sh, como o csh e o tcsh.

Notas:
  • O que eu chamo de string, é uma cadeia de caracteres, como uma palavra ou frase.
  • É importante notar que o menor índice de um vetor ou string é o (zero). Quando são inicializados, este é o primeiro índice.
  • Muitas das características e operações que mostrarei aqui são consequências da própria definição de vetor em shell.

Inicializando:

Vamos começar inicializando um vetor com valores previamente escolhidos:

$ vetor=(zero um dois três quatro)

Podemos também inicializar um vetor com a saída de um comando, como em:

$ UNAME=($(uname -a))
ou
$ UNAME=(`uname -a`)

Mas se em vez de inicializar um vetor já com todos os seus elementos, que tal inicia-lo aos poucos?

Atribuindo um valor a um elemento de "vetor", que terá como índice 2:

$ vetor[2]=Cem

Atribuindo um valor a um elemento de "vetor", que terá como índice 6:

$ vetor[6]=Milhão

Atribuindo um valor a um elemento de "vetor", que terá como índice 3:

$ vetor[3]=Mil

Nota: os índices de um vetor em shell não precisam ser consecutivos, mas estão sempre em ordem, mesmo não precisando ser definidos assim.

Agora iremos para um exemplo que tenho certeza de que todos estamos acostumados, mas que será necessário.

Inicializando uma string;

$ string="zero um dois três quatro"

Acessando elementos:

Num vetor, os elementos são separados por um espaço. Neste caso, o espaço não é um caractere (' '), e sim um separador dos elementos. Quanto aos espaços que eventualmente façam parte dos elementos, estes sim são caracteres.

Exemplo:

Vetor_de_string=("O rato roeu " "a roupa do" Rei "de Roma")

Neste caso, temos quatro elementos no vetor:

Vetor_de_string[0] é "O rato roeu"
Vetor_de_string[1] é "a roupa do"
Vetor_de_string[2] é "Rei"
Vetor_de_string[3] é "de Roma"

Se quisermos recuperar um elemento de um vetor, fazemos:

$ echo $Vetor_de_string[1]

Certo?
Errado.
Se você executar o comando acima, terá a seguinte saída na tela:
O rato roeu[1]

Que não é exatamente o que queremos, que é "a roupa do".
Para contornar este problema, usaremos uma segunda maneira de expressarmos variáveis.

$ echo ${Vetor_de_string[1]}
a roupa do

Agora sim. Estamos indo bem.

Nota:
No bash há, pelo menos, três formas de se expressar uma variável:
  • $var : para strings em geral.
  • ${var} : também para strings, vetores, etc.
  • $((var)) : para elementos numéricos inteiros.

De agora em diante, usaremos a segunda forma para representar vetores.

Antes, deixa eu explicar que $vetor expressa o primeiro elemento de vetor, que é o de índice zero (0). Se você viajar um pouco, vai perceber que uma variável simples é na verdade um vetor de 1 elemento, que têm índice zero!

Exibir todos os elementos de um vetor:

$ echo ${vetor[@]}
ou
$ echo ${vetor[*]}

Exibir todos os índices dos elementos de um vetor:

$ echo ${!vetor[@]}
ou
$ echo ${!vetor[*]}

Transformando um tipo em outro:

Nota: Uma string, na maioria das linguagens, tem o mesmo tratamento que um vetor. Em shell, tratamos estes dois tipos de maneiras um pouco diferentes, como veremos mais adiante.

Se um dia quisermos transformar uma string em um vetor (Onde cada palavra é um elemento)?

Basta executar:

$ string=(${string})

O que acontece neste caso é a transformação do espaço como caractere em espaço como separador de elementos. Legal, não?

Podemos, também, transformar um vetor numa string, com:

$ vetor="${vetor[@]}"

Recursos para o uso de listas:

Removendo elementos:

Se quisermos apagar um vetor, ou um elemento deste?

"Aniquilando" (apagando) um vetor:

$ unset vetor

"Aniquilando" (removendo) um elemento de índice i de um vetor:

$ unset vetor[$i]

Neste segundo caso, os índices dos elementos são "realinhados", de modo a não haver um "buraco vazio" no meio do vetor. Ou seja, se anteriormente eu tinha um vetor com os seguintes índices:

1 3 6 7 9

E decida apagar o elemento de índice 6, ( unset vetor[6] ), ele fica com os seguintes índices:

1 3 7 9

Inserindo elementos:

Inserindo um elemento no final de um vetor:

$ vetor=(${vetor[@]} "$elem")

Inserindo um elemento no começo de um vetor:

$ vetor=( "$elem" ${vetor[@]} )

Neste caso, os índices dos elementos do vetor são redefinidos, como é se pensar.

É aquela velha história: "Se você ultrapassa o segundo lugar, em que posição você fica?". Não sei, mas os outros que ficaram para trás caem uma posição (ou aumentam em 1 o seu índice).

Um problema deste método é que os índices originais são perdidos, dando lugar aos índices consecutivos, começados em zero.

Exibir um intervalo de elementos dentro de um vetor:

Se você quer, por exemplo, exibir somente os elementos do índice i em diante, tente:

$ echo ${vetor[@]:$i}

Mas se você quiser exibir o intervalo fechado de i até i+k, tente:

$ echo ${vetor[@]:$i:((k+1))}

É, este último exemplo foi realmente estranho... Acho que nem eu entendi direito.

Se você quiser exibir todos os elementos até o índice i, excluindo este, faça:

$ echo ${vetor[@]:0:$i}

OBS: Se quiser incluí-lo, substitua $i por ((i+1))

Intervalo fechado do elemento de índice i até o de índice k (com k>=i):

$ echo ${vetor[@]:$i:((k-i+1))}

Bem, acredito que já deu para perceber a sacada dessa sintaxe:

${vetor[@]:índice_inicio:índice_fim+1}

Onde:
  • índice_inicio é o primeiro índice da lista impressa
  • índice_fim é o último índice impresso na lista

Você pode usar esses truques acima também com strings, fazendo as adaptações necessárias, é claro.

Exemplos:

Se você quiser saber qual é o caractere de índice i de uma string, faça:

$ echo ${string:$i:1}

O que isso aí em cima diz?
Exiba, a partir da i-ésima posição de string, 1 elemento, que é o que queremos.

Comprimento de um vetor:

Para saber o "comprimento" de uma string - que é o número de caracteres que ela tem -, faça:

$ echo ${#string}

Isso funciona para vetor também, mas neste caso o que são contados são os elementos do mesmo:

$ echo ${#vetor[@]}
ou
$ echo ${#vetor[*]}

Podemos também misturar os recursos acima, para saber o comprimento do i-ésimo elemento de um vetor:

$ echo ${#vetor[$i]}

Ou para saber qual é o caractere de índice 3 do elemento de índice 2 de um vetor:

$ echo ${vetor[2]:3:1}

Estes são somente alguns recursos. Na página de referência do bash você encontrará muito mais.

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

Leitura recomendada
Nenhum artigo encontrado.

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!!!


Contribuir com comentário


  
Para executar esta ação você precisa estar logado no site, caso contrário, tudo o que for digitado será perdido.
Responsável pelo site: Fábio Berbert de Paula - Conteúdo distribuído sob licença GNU FDL
Site hospedado por:

Viva o Linux

A maior comunidade Linux da América Latina! Artigos, dicas, tutoriais, fórum, scripts e muito mais. Ideal para quem busca auto-ajuda em Linux.