O documento descreve um curso de Shell Script e discute blocos de código, laços (loops) e o comando for do Shell. O texto explica como funcionam blocos de código, laços e for através de exemplos de scripts, discutindo também variáveis e operadores importantes como IFS.
1. ����������������������������������������������������
�����������������������������������������������������������������������������������������������������������������������������������������������������
Papo de Botequim LINUX USER
Curso de Shell Script
Papo de
Botequim V
Blocos de código e laços (ou loops, como preferem alguns)
Dave Hamilton - www.sxc.hu
são o tema do mês em mais uma lição de nosso curso de Shell
Script. Garçom, salta uma boa redondinha, que tô a fim de
refrescar o pensamento! POR JULIO CEZAR NEVES
–
F ala cara! E as idéias estão em
ordem? Já fundiu a cuca ou você
ainda agüenta mais Shell?
Até agora já vimos alguns blocos de
código, como quando te mostrei um
exemplo para fazer um cd para dentro
Güento! Tô gostando muito! Gostei de um diretório:
tanto que até caprichei no exercício
Quadro 1: vira.sh
$ cat vira.sh
#!/bin/bash
#
que você passou. Lembra que você cd lmb 2> /dev/null || # vira - vi resguardando
me pediu para fazer um programa { # arquivo anterior
que recebe como parâmetro o nome mkdir lmb # Verifica se algum parâmetro foi
de um arquivo e que quando execu- cd lmb # passado
tado salva esse arquivo com o nome } if [ “$#” -ne 1 ]
original seguido de um til (~) e o then
abre dentro do vi? O fragmento contido entre as duas echo “Erro -> Uso: $0 U
– Claro que lembro, me mostre e expli- chaves ({}) forma um bloco de código. <arquivo>”
que como você fez. Também nesse exercício que acabamos exit 1
– Beleza, dá uma olhada no quadro 1 de ver, em que salvamos o arquivo antes fi
– É, beleza! Mas me diz uma coisa: por de editá-lo, existem vários blocos de Arq=$1
que você terminou o programa com código compreendidos entre os coman- # Caso o arquivo não exista, não
um exit 0? dos then e fi do if. Um bloco de código # há cópia a ser salva
– Eu descobri que o número após o também pode estar dentro de um case if [ ! -f “$Arq” ]
exit indica o código de retorno do ou entre um do e um done. then
programa (o $?, lembra?) e assim, – Peraí, Julio, que do e done são esses? vi $Arq
como a execução foi bem sucedida, Não me lembro de você ter falado exit 0
ele encerra com o $?=0. Porém, se nisso, e olha que estou prestando fi
você observar, verá que caso o pro- muita atenção... # Se eu não puder alterar o
grama não tenha recebido o nome do – Pois é, ainda não tinha falado porque #arquivo, vou usar o vi para que?
arquivo ou caso o operador não tenha não havia chegado a hora certa. if [ ! -w “$Arq” ]
permissão de gravação nesse arquivo, Todas as instruções de loop ou laço then
o código de retorno ($?) seria dife- executam os comandos do bloco com- echo “Você não tem permissão U
rente do zero. preendidos entre um do e um done. As de escrita em $Arq”
– Grande garoto, aprendeu legal, mas é instruções de loop ou laço são for, while exit 2
bom deixar claro que exit 0, simples- e until , que serão explicadas uma a fi
mente exit ou não colocar exit produ- uma a partir de hoje. # Já que está tudo OK, vou
zem igualmente um código de retorno # salvar a cópia e chamar o vi
($?) igual a zero. Agora vamos falar O comando For cp -f $Arq $Arq~
sobre as instruções de loop ou laço, Se você está habituado a programar, vi $Arq
mas antes vou passar o conceito de certamente já conhece o comando for, exit 0
bloco de código. mas o que você não sabe é que o for,
�����������������������������
www.linuxmagazine.com.br edição 05 89
��������������������������������������������������������������������������������
2. ���������������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������������������������
LINUX USER Papo de Botequim
que é uma instrução intrínseca do Shell Então vamos executá-lo: $ echo “$IFS” | od -h
(isso significa que o código fonte do 0000000 0920 0a0a
comando faz parte do código fonte do $ testefor1 0000004
Shell, ou seja, em bom programês é um ArqDoDOS.txt1:confuso:incusu:
built-in), é muito mais poderoso que os logado:musexc:musicas:musinc: Isto é, mandei a variável (protegida
seus correlatos das outras linguagens. muslist:$ da interpretação do Shell pelas aspas)
Vamos entender a sua sintaxe, pri- para um dump hexadecimal (od -h). O
meiro em português e, depois, como Como você viu, o Shell transformou resultado pode ser interpretado com a
funciona pra valer. Olhe só: o asterisco (que odeia ser chamado tabela abaixo:
de asterístico) em uma lista de arqui-
para var em val1 val2 ... valn vos separados por espaços em branco. Tabela 1: Resultado do od -h
faça Quando o for viu aquela lista, disse:
cmd1 “Opa, listas separadas por espaços é Valor Hexadecimal Significado
cmd2 comigo mesmo!” 09 <TAB>
cmdn O bloco de comandos a ser executado 20 <ESPAÇO>
feito era somente o echo, que com a opção -n 0a <ENTER>
listou a variável $Arq seguida de dois-
Onde a variável var assume cada um pontos (:), sem saltar a linha. O cifrão
dos valores da lista val1 val2 ... valn e, ($) do fi nal da linha da execução é o O último 0a foi proveniente do
para cada um desses valores, executa o prompt, que permaneceu na mesma <ENTER> dado ao fi nal do comando.
bloco de comandos formado por cmd1, linha também em função da opção -n. Para melhorar a explicação, vamos ver
cmd2 e cmdn. Agora que já vimos o Outro exemplo simples (por enquanto): isso de outra forma:
significado da instrução em português,
vejamos a sintaxe correta: $ cat testefor2 $ echo “:$IFS:” | cat -vet
#!/bin/bash : ^I$
for var in val1 val2 ... valn # 2o. Programa didático para :$
do # entender o for
cmd1 for Palavra in Linux Magazine U No comando cat, a opção -e repre-
cmd2 do Brasil senta o <ENTER> como um cifrão ($)
cmdn do e a opção -t representa o <TAB> como
done echo $Palavra um ^I. Usei os dois-pontos (:) para mos-
done trar o início e o fi m do echo. E dessa
Vamos aos exemplos, para entender forma, pudemos notar que os três carac-
direito o funcionamento deste comando. E executando temos: teres estão presentes naquela variável.
Vamos escrever um script para listar Agora veja você: traduzindo, IFS sig-
todos os arquivos do diretório, separa- $ testefor2 nifica separador entre campos. Uma vez
dos por dois-pontos, mas antes veja isso: Linux entendido isso, eu posso afi rmar que o
Magazine comando for não usa apenas listas sepa-
$ echo * do radas por espaços em branco, mas sim
ArqDoDOS.txt1 confuso incusu Brasil pelo conteúdo da variável $IFS, cujo
logado musexc musicas musinc valor padrão são os caracteres que aca-
muslist Como você viu, esse exemplo é bamos de ver. Para comprovarmos isso,
tão bobo e simples como o anterior, vamos continuar mexendo em nossa
Isto é, o Shell viu o asterisco (*), mas serve para mostrar o comporta- CDTeca, escrevendo um script que
expandiu-o com o nome de todos os mento básico do for. Veja só a força do recebe o nome do artista como parâme-
arquivos do diretório e o comando echo comando: ainda estamos na primeira tro e lista as músicas que ele toca. Mas
jogou-os para a tela separados por espa- possibilidade de sintaxe e já estou mos- primeiramente vamos ver como está o
ços em branco. Visto isso, vamos resol- trando novas formas de usá-lo. Lá atrás nosso arquivo musicas:
ver o problema a que nos propusemos: eu havia falado que o for usava listas
separadas por espaços em branco, mas $ cat musicas
$ cat testefor1 isso é uma meia-verdade, só para facili- album 1^Artista1~Musica1: U
#!/bin/bash tar a compreensão. Na verdade, as listas Artista2~Musica2
# 1o. Programa didático para não são obrigatoriamente separadas por album 2^Artista3~Musica3: U
# entender o for espaços. Mas antes de prosseguir, pre- Artista4~Musica4
for Arq in * ciso te mostrar como se comporta uma album 3^Artista5~Musica5: U
do variável do sistema chamada de IFS, ou Artista6~Musica6
echo -n $Arq: Inter Field Separator Veja no exemplo a album 4^Artista7~Musica7: U
done seguir seu conteúdo: Artista1~Musica3
�����������������������������
90 edição 05 www.linuxmagazine.com.br
��������������������������������������������������������������������������������
3. ����������������������������������������������������
�����������������������������������������������������������������������������������������������������������������������������������������������������
Papo de Botequim LINUX USER
album 5^Artista9~Musica9: U o $1 seria Perereca e o resto desse lindo $ listartista Artista1
Artista10~Musica10 nome seria ignorado na pesquisa. Musica1
Para que isso não ocorra, eu deveria Musica3
Em cima desse “leiaute” desenvolve- passar o nome do artista entre aspas
mos o script a seguir: (”) ou trocar $1 por $* (que representa Veja uma segunda sintaxe para o for:
todos os parâmetros passados), que é
$ cat listartista a melhor solução, mas nesse caso eu for var
#!/bin/bash teria que modificar a crítica dos parâ- do
# Dado um artista, mostra as metros e o grep. A nova versão não cmd1
# suas músicas seria se eu passei um parâmetro, mas cmd2
if [ $# -ne 1 ] sim se passei pelo menos um parâ- cmdn
then metro. Quanto ao grep, veja só o que done
echo Você deveria ter U aconteceria após a substituição do $*
passado um parâmetro pelos parâmetros: Ué, sem o in, como ele vai saber que
exit 1 valor assumir? Pois é, né? Esta constru-
fi echo “$ArtMus” | grep perereca U ção, à primeira vista, parece esquisita,
IFS=” & peteleca mas é bastante simples. Neste caso, var
:” assumirá um a um cada parâmetro pas-
for ArtMus in $(cut -f2 -d^ U Isso gera um erro. O correto é: sado para o programa. Como exemplo
musicas) para entender melhor, vamos fazer um
do echo “$ArtMus” | grep -i U script que receba como parâmetro um
echo “$ArtMus” | grep $1 && U “perereca & peteleca” monte de músicas e liste seus autores:
echo $ArtMus | cut -f2 -d~
done Aqui adicionamos a opção -i para $ cat listamusica
que a pesquisa ignorasse maiúsculas e #!/bin/bash
O script, como sempre, começa tes- minúsculas. As aspas foram inseridas # Recebe parte dos nomes de
tando se os parâmetros foram passados para que o nome do artista fosse visto # músicas como parâmetro e
corretamente, em seguida o IFS foi confi- como uma só cadeia de caracteres. # lista os intérpretes. Se o
gurado para <ENTER> e dois-pontos (:) Falta consertar o erro dele ter listado o # nome for composto, deve
(como demonstram as aspas em linhas Artista10. O melhor é dizer ao grep que a # ser passado entre aspas.
diferentes), porque é ele quem separa os cadeia de caracteres está no início (^) de # ex. “Eu não sou cachorro não”
blocos Artistan~Musicam. Desta forma, $ArtMus e que logo após vem um til (~). É # “Churrasquinho de Mãe”
a variável $ArtMus irá receber cada um preciso redirecionar a saída do grep para / #
desses blocos do arquivo (repare que o dev/null para que os blocos não sejam lis- if [ $# -eq 0 ]
for já recebe os registros sem o álbum tados. Veja a nova cara do programa: then
em virtude do cut na sua linha). Caso echo Uso: $0 musica1 U
encontre o parâmetro ($1) no bloco, o $ cat listartista [musica2] ... [musican]
segundo cut listará somente o nome da #!/bin/bash exit 1
música. Vamos executar o programa: # Dado um artista, mostra as fi
# suas musicas IFS=”
$ listartista Artista1 # Versao 2 :”
Artista1~Musica1 if [ $# -eq 0 ] for Musica
Musica1 then do
Artista1~Musica3 echo Voce deveria ter U echo $Musica
Musica3 passado pelo menos um parametro Str=$(grep -i “$Musica” U
Artista10~Musica10 exit 1 musicas) ||
Musica10 fi {
IFS=” echo “ Não U
Êpa! Aconteceram duas coisas inde- :” encontrada”
sejáveis: os blocos também foram lista- for ArtMus in $(cut -f2 -d^ U continue
dos, e a Musica10 idem. Além do mais, musicas) }
o nosso arquivo de músicas está muito do for ArtMus in $(echo “$Str” U
simples: na vida real, tanto a música echo “$ArtMus” | grep -i U | cut -f2 -d^)
quanto o artista têm mais de um nome. “^$*~” > /dev/null && echo U do
Suponha que o artista fosse uma dupla $ArtMus | cut -f2 -d~ echo “ $ArtMus” | U
sertaneja chamada Perereca & Peteleca done grep -i “$Musica” | cut -f1 -d~
(não gosto nem de dar a idéia com receio done
que isso se torne realidade). Nesse caso, O resultado é: done
�����������������������������
www.linuxmagazine.com.br edição 05 91
��������������������������������������������������������������������������������
4. ���������������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������������������������
LINUX USER Papo de Botequim
Da mesma forma que os outros, come- Ou na forma mais completa do seq: Repare que o incremento saiu do
çamos o exercício com uma crítica sobre corpo do for e passou para o bloco de
os parâmetros recebidos, em seguida for i in $(seq 0 3 9) código; repare também que, quando
fizemos um for em que a variável do usei o let, não foi necessário iniciali-
$Musica receberá cada um dos parâme- echo -n “$i “ zar a variável $i. Veja só os comandos a
tros passados, colocando em $Str todos done seguir, digitados diretamente no prompt,
os álbuns que contêm as músicas dese- 0 3 6 9 para demonstrar o que acabo de falar:
jadas. Em seguida, o outro for pega cada
bloco Artista~Musica nos registros que A outra forma de fazer isso é com $ echo $j
estão em $Str e lista cada artista que uma sintaxe muito semelhante ao for da $ let j++
toca aquela música. Vamos executar o linguagem C, como vemos a seguir: $ echo $j
programa para ver se funciona mesmo: 1
for ((var=ini; cond; incr))
$ listamusica musica3 Musica4 U do Ou seja, a variável $j sequer existia e
“Egüinha Pocotó” cmd1 no primeiro let assumiu o valor 0 (zero)
musica3 cmd2 para, após o incremento, ter o valor 1.
Artista3 cmdn Veja só como as coisas ficam simples:
Artista1 done
Musica4 for arq in *
Artista4 Onde var=ini significa que a variá- do
Egüinha Pocotó vel var começará de um valor inicial let i++
Não encontrada ini; cond significa que o loop ou laço for echo “$i -> $Arq”
será executado enquanto var não atingir done
A listagem ficou feinha porque ainda a condição cond e incr significa o incre- 1 -> ArqDoDOS.txt1
não sabemos formatar a saída; mas mento que a variável var sofrerá a cada 2 -> confuso
qualquer dia desses, quando você sou- passada do loop. Vamos aos exemplos: 3 -> incusu
ber posicionar o cursor, trabalhar com 4 -> listamusica
cores etc., faremos esse programa nova- for ((i=1; i<=9; i++)) 5 -> listartista
mente usando todas essas perfumarias. do 6 -> logado
A esta altura dos acontecimentos, echo -n “$i “ 7 -> musexc
você deve estar se perguntando: “E done 8 -> musicas
aquele for tradicional das outras lingua- 1 2 3 4 5 6 7 8 9 9 -> musinc
gens em que ele sai contando a partir 10 -> muslist
de um número, com um determinado A variável i partiu do valor inicial 1, o 11 -> testefor1
incremento, até alcançar uma condi- bloco de código (aqui somente o echo) 12 -> testefor2
ção?”. E é aí que eu te respondo: “Eu será executado enquanto i for menor ou
não te disse que o nosso for é mais por- igual (<=) a 9 e o incremento de i será – Pois é amigo, tenho certeza que você
reta que o dos outros?” Para fazer isso, de 1 a cada passada do loop. já tomou um xarope do comando
existem duas formas. Com a primeira Repare que no for propriamente dito for. Por hoje chega, na próxima vez
sintaxe que vimos, como no exemplo: (e não no bloco de código) não coloquei em que nos encontrarmos falaremos
um cifrão ($) antes do i e a notação sobre outras instruções de loop, mas
for i in $(seq 9) para incrementar (i++) é diferente do eu gostaria que até lá você fi zesse um
do que vimos até agora. O uso de parênte- pequeno script para contar a quanti-
echo -n “$i “ ses duplos (assim como o comando let) dade de palavras de um arquivo texto,
done chama o interpretador aritmético do cujo nome seria recebido como parâ-
1 2 3 4 5 6 7 8 9 Shell, que é mais tolerante. metro. Essa contagem tem que ser
Só para mostrar como o let funciona feita com o comando for, para se habi-
A variável i assumiu os valores intei- e a versatilidade do for, vamos fazer a tuar ao seu uso. Não vale usar o wc -w.
ros entre 1 a 9 gerados pelo comando mesma coisa, mas omitindo a última Aê Chico! Traz a saideira! ■
seq e a opção -n do echo foi usada para parte do escopo do for, passando-a para
não saltar uma linha a cada número lis- o bloco de código: Julio Cezar Neves é Analista de
SOBRE O AUTOR
tado. Ainda usando o for com seq: Suporte de Sistemas desde 1969 e tra-
for ((; i<=9;)) balha com Unix desde 1980, quando
for i in $(seq 4 9) do participou do desenvolvimento do
do let i++ SOX, um sistema operacional similar
echo -n “$i “ echo -n “$i “ ao Unix produzido pela Cobra Com-
putadores. Pode ser contatado no
done done
e-mail julio.neves@gmail.com
4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
�����������������������������
92 edição 05 www.linuxmagazine.com.br
��������������������������������������������������������������������������������