domingo, 17 de janeiro de 2010

Automatize sequências de comandos no prompt do shell com laços

Um comando de iteração, ou laço, permite que um conjunto de instruções seja executado repetidamente até que ocorra uma certa condição. No Linux o interpretador de comandos Bash, da mesma forma que às principais linguagens de programação, também possui os comandos básicos de iteração e são os comandos for, while e until.

O comando for é o mais simples dos três, utiliza um contador com um início e fim pré-definido e o incremento do passo. Durante cada passo a variável "argumento" recebe o valor de cada membro da "lista". O conjunto de instruções que será executado repetidamente deve ficar entre as palavras reservadas do e done. Sua sintaxe básica é apresentada abaixo:

for argumento in lista
do
comando(s)
done


Como vamos executar todo o comando em uma única linha no prompt do shell, então é necessário alguns ponto-e-vírgulas:

for argumento in lista; do comando(s); done


Veja alguns exemplos de lista para compor um contador para a repetição. Nos casos abaixo todos produzem o mesmo efeito, contando de 1 à 10. Em dois exemplos foi usado o utilitário seq, que imprime uma sequência de números:

for i in 1 2 3 4 5 6 7 8 9 10; do echo -n "$i "; done
for i in $(seq 10); do echo -n "$i "; done
for i in `seq 10`; do echo -n "$i "; done
for i in {1..10}; do echo -n "$i "; done
for ((i=1; i<=10; i++)); do echo -n "$i "; done


A lista de valores pode conter palavras ao invés de números, veja estes exemplos. O segundo exemplo utiliza um arquivo que contém as palavras:

for dia in Segunda Terça Quarta Quinta Sexta; do echo $dia; done
for dia in `cat semana.txt`; do echo $dia; done


O incremento do passo pode ser mais de 1 em 1. Os exemplos abaixo produzem o mesmo efeito, contando de 1 à 16 com passo 2:

for ((i=1;i<=16;i+=2)); do echo $i; done
for i in $(seq 1 2 16); do echo $i; done
for i in `seq 1 2 16`; do echo $i; done
for i in 1 3 5 7 9 11 13 15; do echo $i; done
foi i in {1..16..2}; do echo $i; done # A partir do bash versão 4.


É possível também utilizar duas variáveis no contador, na sintaxe semelhante à linguagem C:

for ((i=1, j=1; i<=10; i++, j++)); do echo -n "$i-$j "; done


Exemplos práticos com o uso do comando for:

for i in *.*; do novo="$(echo "$i" | sed 'y/ÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÇÑàáâãäåèéêëìíîïòóôõöùúûüçñ/
AAAAAAEEEEIIIIOOOOOUUUUCNaaaaaaeeeeiiiiooooouuuucn/')"; mv "$i" "$novo"; done

for i in `ls *-6_*.pdf`; do mv $i ${i/-6_/-06_}; done

n=1; for nome in $(awk 'BEGIN{FS=":"}{print $1}' < /etc/passwd ); do echo "USUARIO #$n = $nome";
let n+=1; done

for file in *.mp3; do mkdir -p "$(mp3info -p "%a/%l" "$file")" && mv "$file" "$(mp3info -p
"%a/%l/%t.mp3" "$file")"; done

for x in $(lynx -dump http://marx.vanderlinden.com.br/sem/2009.1/fi.php | awk '{print $2}' | grep
.pdf); do wget -c $x; done

for arquivo in $( find /usr/local/bin/ -type f -name '*' | sort ); do strings -f $arquivo | grep
"GPL" | sed -e "s%/usr/local/bin/%%"; done


O comando while testa por uma condição no início do laço e mantém a iteração enquanto esta condição é verdadeira (retorna 0 quando falso). Diferentemente do comando for, o comando while é usado quando o número de repetições não é conhecido de antemão. O conjunto de instruções que será executado repetidamente deve ficar entre as palavras reservadas do e done. Sua sintaxe básica é apresentada abaixo:

while condição
do
comando(s)
done


Mais uma vez, como vamos executar todo o comando em uma única linha, no prompt do shell, então é necessário alguns ponto-e-vírgulas:

while condição; do comando(s); done


Para o comando while executar o laço a condição já deve existir. Veja este exemplo onde é inicializada a variável de controle antes do comando while (Obs.: é necessário os espaços dentro dos colchetes pois é um teste de condição):

i=1; while [ $i -le 10 ]; do echo -n "$i "; let i+=1; done


Ou, produzindo o mesmo resultado:

i=1; while ((i<=10)); do echo $i; ((i+=1)); done


Uma outra forma, por exemplo, é suprir a entrada com o conteúdo de um arquivo:

cat semana.txt | while read linha; do echo -n "$linha "; done


Ou, produzindo o mesmo resultado:

while read linha; do echo -n "$linha "; done < <(cat semana.txt)


Exemplos práticos com o uso do comando while:

find /usr/share/ -regextype posix-egrep -iregex '.*\.(jpg|jpeg|gif|png|tif|tiff|bmp|svg)' -type f
-print | while read IMAGEM; do convert -thumbnail 120x120 -compress JPEG -quality 20 -delete 1--1
"$IMAGEM" thumbs/"$(echo "${IMAGEM}" | sed 's/.*\///g')".jpg; done

while read nome; do echo "USER #$n = $nome"; let n+=1; done < <(awk 'BEGIN{FS=":"}{print $1}'
< /etc/passwd )

seq 1 100 | while read i; do echo -n "texto${i}.txt "; touch texto${i}.txt 2>&1; done

while read i; do echo -n "texto${i}.txt "; touch texto${i}.txt 2>&1; done < <(seq 1 100)

while read arquivo; do strings -f $arquivo | grep "GPL" | sed -e "s%/usr/local/bin/%%";
done < <( find /usr/local/bin/ -type f -name '*' | sort )


O comando until testa por uma condição no início do laço e mantém esta iteração enquanto a condição for falsa (diferentemente do comando similar em outras linguagens), sendo o oposto do comando while. Também o comando until é usado quando o número de repetições não é conhecido de antemão. O conjunto de instruções que será executado repetidamente deve ficar entre as palavras reservadas do e done. Sua sintaxe básica é apresentada abaixo:

until condição
do
comando(s)
done


Como sempre, vamos executar todo o comando em uma única linha, no prompt do shell, então é necessário alguns ponto-e-vírgulas:

until condição; do comando(s); done


Também como o comando while, para o until executar o laço a condição já deve existir. Neste exemplo é inicializada a variável de controle antes do comando until:

i=1; until [ $i -gt 10 ]; do echo $i; let i+=1; done


Ou, produzindo o mesmo resultado:

i=1; until ((i>10)); do echo $i; ((i+=1)); done


Da mesma forma que demonstrado no comando while, também é possível suprir a entrada com o conteúdo de um arquivo.

Exemplos práticos com o uso do until:

until [ -e teste.txt ]; do echo -e "Crie o arquivo teste.txt\n"; sleep 5; done

until [ $(ls -l eventos.log | awk '{print $5}') -gt 5000 ]; do echo "Arquivo log ainda pequeno";
sleep 1; done


Nestes três comandos de iteração é possível interromper o comando ou somente pular o passo atual e continuar no próximo. Para isso usa-se respectivamente os comandos break e continue na posição desejada entre o do e o done. Obviamente para ter este controle certamente vai ser necessário o uso de um comando condicional, como o if.

Como escolher entre os laços for, while ou until? Pode ser possível conseguir o mesmo efeito com os três comandos de laço. Veja os comandos abaixo, todos produzem o mesmo resultado, então provavelmente o melhor será aquele que visualmente for mais fácil de entender o algoritmo. Mas observe que o comando for é mais utilizado quando o número de iterações é conhecido e os comandos while e until servem para um número impreciso de iterações:

for ((i=1; i<=10; i++)); do echo "$i "; done
i=1; while ((i<=10)); do echo $i; ((i+=1)); done
i=1; until ((i>10)); do echo $i; ((i+=1)); done

Nenhum comentário:

Postar um comentário