você está aqui: Home → Colunistas → Cantinho do Shell
Por Rodrigo Bernardo Pimentel
Data de Publicação: 21 de Junho de 2007
É comum queremos executar a mesma função, ou uma função parecida,
sobre uma série de argumentos, um por vez.
Em bash, há duas estruturas básicas pra isso: for e while.
O for, em bash, é diferente do for em linguagens como C ou
Perl. Nessas linguagens, o for é simplesmente um while mais completo. Em
bash, o for atua sobre uma seqüência de parâmetros (não necessariamente
numéricos ou seqüenciais). Por exemplo:
[rbp@muppets ~]$ for i in 1 2 3 4 5; do echo $i; done 1 2 3 4 5 [rbp@muppets ~]$
Ou,
[root@muppets ~]$ for login in rbp sizinha queiroz; do adduser $login; done [root@muppets ~]$
Você pode inclusive usar o for a partir de uma saída de comando. Por exemplo, digamos que você tenha um arquivo com uma série de nomes de usuários a serem acrescentados ao sistema, como no exemplo acima:
[root@muppets ~]$ for login in `cat /tmp/usuarios`; do adduser $login; done [root@muppets ~]$
O while, por sua vez, tem funcionamento semelhante à maioria das
linguagens procedurais mais comuns. Um comando ou bloco de comandos continua
a ser executado enquanto uma determinada condição for verdadeira. Por
exemplo, imitando o exemplo acima:
[rbp@muppets ~]$ while [ $i -le 5 ]; do echo $i; i=$(($i + 1)); done 1 2 3 4 5 [rbp@muppets ~]$
Aos poucos:
while [ $i -le 5 ]
O while deve ser seguido de "verdadeiro" ou "falso". Se for
"verdadeiro", o bloco é executado e o teste é repetido. Se for falso, não.
No caso, [ $i -le 5 ] é uma forma de usarmos o comando
test. Esse comando testa uma expressão e retorna verdadeiro ou
falso. Poderia ser escrito como
while test $i -le 5
$i -le 5 é a expressão testada. O programa test aceita alguns
operadores. Entre eles:
| -lt (less than) | primeiro argumento é menor do que o segundo |
| -le (less or equal) | primeiro argumento é menor ou igual ao segundo |
| -gt (greater than) | primeiro argumento é maior do que o segundo |
| -ge (greater or equal) | primeiro argumento é maior ou igual ao segundo |
| = | primeiro argumento é igual ao segundo |
| != | primeiro argumento é diferente do segundo |
O programa test pode também fazer testes unários, ou seja, com
só um argumento. Por exemplo,
arq="/tmp/arquivo"
tmp=$arq
i=1
while [ -e $tmp ]; do
i=$(($i+1))
tmp=$arq$i
done
touch $tmp
Esse scriptzinho (note que não o fiz na linha de comando, mas indentado, para usá-lo a partir de um arquivo; funciona dos dois jeitos) só sai do loop quando achar um nome de arquivo que não exista. Ou, de forma mais didática, "enquanto existir (-e) o arquivo cujo nome está na variável $tmp, ele continua executado o bloco de comandos".
Alguns dos operadores unários mais comuns são:
| -e | arquivo ou diretório existe |
| -f | é um arquivo (em oposição a ser um diretório) |
| -d | é um diretório |
| -x | arquivo tem permissão de execução para o usuário atual |
| -w | arquivo tem permissão de escrita pelo usuário atual |
| -r | arquivo tem permissão de leitura pelo usuário atual |
Para mais detalhes, man test.
Continuando:
do echo $i
Logo após um while <teste>, é preciso iniciar o primeiro comando
com do. Os seguintes (se houver), não.
A próxima linha mostra um exemplo de algo que não tem nada a ver com
a estrutura do while em si, mas é um truquezinho legal de bash:
i=$(($i + 1))
A construção $((expressão)) é um operador matemático em bash. Isso
é expandido para o resultado da expressão. Por exemplo,
[rbp@muppets ~]$ echo $((2 + 3)) = 5 [rbp@muppets ~]$ echo $((2 - 3)) # Funciona com números negativos = -1 [rbp@muppets ~]$ echo $((2 * 3)) = 6 [rbp@muppets ~]$ echo $((10 / 2)) = 5 [rbp@muppets ~]$ echo $((3 / 2)) # Não usa casas decimais = 1 [rbp@muppets ~]$ =
Mas, como diz um amigo meu, voltando...
done
Isso termina "oficialmente" o loop while. A propósito, como pode
ter sido notado, termina o for também.
Como foi dito acima, o while espera "verdadeiro" ou "falso". Eu
nunca disse que esperava isso só do programa test :)
Com efeito, qualquer expressão pode ser usada, e seu valor de
retorno será utilizado para determinar se é "verdadeiro" ou "falso". Para
quem não sabe, todo programa em Unix retorna um valor ao terminar sua
execução. Normalmente, se tudo correu bem o valor retornado é 0 (zero). Se
há algum erro, o valor retornado, diferente de zero, indica o tipo de erro
(veja as manpages dos programas; man fetchmail seção exit codes é
um bom exemplo). Portanto, ao contrário do que programadores de C ou Perl
poderiam achar intuitivo (dentro de um while, ou uma condição em geral),
um programa que retorna 0 é considerado "verdadeiro" aos olhos do "while``.
Assim, podemos fazer:
while w | grep -qs rbp; do
sleep 5s
done
echo 'rbp acaba de sair do sistema!'
Nesse exemplo, o while checa o retorno da expressão w | grep -qs rbp. Isso retorna "verdadeiro" quando o grep acha rbp na saída do comando w. Toda vez que achar, espera 5 segundos e checa de novo. Quando não achar, sai do loop e mostra um aviso de que a última sessão do rbp foi fechada.
Pra finalizar: se você quiser fazer um loop infinito, pode usar :
(dois pontos) como condição sempre verdadeira:
while : ; do
echo 'Emacs rules!'
done
Isso vai imprimir uma constatação sábia infinitamente, até você usar C-c (Ctrl + C). Normalmente, isso é utilizado com alguma condição de parada.
Esta dica foi publicada originalmente na Dicas-L em 16 de agosto de 2000