\
~) e colocaria este arquivo dentro do vi?
- Claro que lembro, me mostre e explique como você fez.
exit 0?
- Ahhh! Eu descobri que o número após o exit resultará no código de retorno do programa (o $?, lembra?), e desta forma, como foi tudo bem sucedido, ele encerraria com o $? = 0. Porém se você observar, verá que caso o programa não tenha recebido o nome do arquivo ou caso o operador não tivesse direito de gravação sobre este arquivo, o código de retorno ($?) seria diferente do zero.
- Grande garoto, aprendeu legal, mas é bom deixar claro que exit 0, simplesmente exit ou não colocar exit, produzem igualmente um código de retorno ($?) igual a zero. Agora vamos falar sobre as instruções de loop ou laço, mas antes vou passar o conceito de bloco de programa.
Até agora já vimos alguns blocos de programa. Quando te mostrei um exemplo para fazer um cd para dentro de um diretório que era assim:
cd lmb 2> /dev/null ||
{
mkdir lmb
cd lmb
}
O fragmento contido entre as duas chaves ({}), forma um bloco de comandos. Também neste exercício que acabamos de ver, em que salvamos o arquivo antes de editá-lo, existem vários blocos de comandos compreendidos entre os then e os fi do if.
Um bloco de comandos também pode estar dentro de um case, ou entre um do e um done.
- Peraí Julio, que do e done é esse, não me lembro de você ter falado nisso e olha estou prestando muita atenção...
- Pois é, ainda não havia falado porque não havia chegado o momento propício. Todas as instruções de loop ou laço, executam os comandos do bloco compreendido entre o do e o done.
for, o while e o until que passarei a te explicar uma-a-uma a partir de agora.
for, mas o que você não sabe é que o for, que é uma instrução intrinseca do Shell (isto significa que o código fonte do comando faz parte do código fonte do Shell, ou seja em bom programês é um built-in), é muito mais poderoso que os seus correlatos das outras linguagens.
Vamos entender a sua sintaxe, primeiramente em português e depois como funciona no duro.
para var em val1 val2 ... valn
faça
cmd1
cmd2
cmdn
feito
Onde a variável var assume cada um dos valores da lista val1 val2 ... valn e para cada um desses valores executa o bloco de comandos formado por cmd1, cmd2 e cmdn
Agora que já vimos o significado da instrução em português, vejamos a sintaxe correta:
for var in val1 val2 ... valn
do
cmd1
cmd2
cmdn
done
Vamos direto para os exemplos, para entender direito o funcionamento deste comando. Vamos escrever um script para listar todos os arquivos do nosso diretório separados por dois-pontos, mas primeiro veja:
*) expandindo-o com o nome de todos os arquivos do diretório e o comando echo jogou-os para a tela separados por espaços em branco. Visto isso vamos ver como resolver o problema a que nos propuzemos:
for viu aquela lista, ele disse: "Opa, lista separadas por espaços é comigo mesmo!"
O bloco de comandos a ser executado era somente o echo, que com a opção -n listou a variável $Arq seguida de dois-pontos (:), sem saltar a linha. O cifrão ($) do final da linha da execução é o prompt. que permaneceu na mesma linha também em função da opção -n.
Outro exemplo simples (por enquanto):
for.
Veja só a força do for: ainda estamos na primeira sintaxe do comando e já estou mostrando novas formas de usá-lo. Lá atrás eu havia falado que o for usava listas separadas por espaços em branco, mas isso é uma meia verdade, era só para facilitar a compreensão.
No duro, as listas não são obrigatóriamente separadas por espaços mas antes de prosseguir, deixa eu te mostrar como se comporta uma variável do sistema chamada de $IFS. Repare seu conteúdo:
od -h) e resultou:
| Conteúdo da Variável $IFS | |
|---|---|
0a |
<ENTER> |
| Hexadecimal | Significado |
09 |
<TAB> |
20 |
<ESPAÇO> |
0a foi proveniente do <ENTER> dado ao final do comando. Para melhorar a explicação, vamos ver isso de outra forma:
No comando cat, a opção -e representa o <ENTER> como um cifrão ($) e a opção -t representa o <TAB> como um ^I. Usei os dois-pontos (:) para mostrar o início e o fim do echo. E desta forma, mais uma vez pudemos notar que os três caracteres estão presentes naquela variável.
IFS significa Inter Field Separator ou, traduzindo, separador entre campos. Uma vez entendido isso, eu posso afirmar (porque vou provar) que o comando for não usa listas separadas por espaços em branco, mas sim pelo conteúdo da variável $IFS, cujo valor padrão (default) são esses caracteres que acabamos de ver. Para comprovarmos isso, vamos mostrar um script que recebe o nome do artista como parâmetro e lista as músicas que ele executa, mas primeiramente vamos ver como está o nosso arquivo musicas:
IFS foi setado para <ENTER> e dois-pontos (:) (como demonstram as aspas em linha diferentes), porque é ele que separa os blocos Artistan~Musicam. Desta forma, a variável $ArtMus irá receber cada um destes blocos do arquivo (repare que o for já recebe os registros sem o álbum em virtude do cut na sua linha). Caso encontre o parâmetro ($1) no bloco, o segundo cut listará somente o nome da música. Vamos executá-lo:
Musica10 idem. Além do mais, o nosso arquivo de músicas está muito simples, na vida real, tanto a música quanto o artista têm mais de um nome. Suponha que o artista fosse uma dupla sertaneja chamada Perereca & Peteleca (não gosto nem de dar a idéia com receio que isso se torne realidade:). Neste caso o $1 seria Perereca e o resto deste lindo nome seria ignorado na pesquisa.
Para que isso não ocorresse, eu deveia passar o nome do artista entre aspas (") ou alterar $1 por $@ (que significa todos os parâmetros passados), que é a melhor solução, mas neste caso eu teria que modificar a crítica dos parâmetros e o grep. A nova crítica não seria se eu passei um parâmetro, mas pelo menos um parâmetro e quanto ao grep, veja só o que resultaria após a substituição do $* (que entraria no lugar do $1) pelos parâmetros:
echo "$ArtMus" | grep perereca & peteleca
O que resultaria em erro. O correto seria:
echo "$ArtMus" | grep -i "perereca & peteleca"
Onde foi colocado a opção -i para que a pesquisa ignorasse maiúsculas e minúsculas e as aspas também foram inseridas para que o nome do artista fosse visto como uma só cadeia monolítica.
Ainda falta consertar o erro dele ter listado o Artista10. Para isso o melhor é dizer ao grep que a cadeia está no início de $ArtMus (a expressão regular para dizer que está no início é ^) e logo após vem um til (~). É necessário também que se redirecione a saída do grep para /dev/null para que os blocos não sejam mais listados. Veja então a nova (e definitiva) cara do programa:
for var
do
cmd1
cmd2
cmdn
done
- Ué, sem o in como ele vai saber que valor assumir?
- Pois é, né? Esta construção a primeira vista parece xquisita mas é bastante simples. Neste caso, var assumirá um-a-um cada um dos parâmetros passados para o progama.
Vamos logo aos exemplos para entender melhor. Vamos fazer um script que receba como parâmetro um monte de músicas e liste seus autores:
for em que a variável $Musica receberá cada um dos parâmetros passados, colocando em $Str todos os álbuns que contém as músicas passadas. Em seguida, o outro for pega cada bloco Artista~Musica nos registros que estão em $Str e lista cada artista que execute aquela música.
Como sempre vamos executá-lo para ver se funciona mesmo:
for tradicional das outras linguagens em que ele sai contando a partir de um número, com um determinado incremento até alcançar uma condição?"
E é aí que eu te respondo: "Eu não te disse que o nosso for é mais porreta que os outros?" Para fazer isso existem duas formas:
1 - Com a primeira sintaxe que vimos, como nos exemplos a seguir direto no prompt:
i assumiu os inteiros de 1 a 9 gerados pelo comando seq e a opção -n do echo foi usada para não saltar linha a cada número listado (sinto-me ecologicamente correto por não gastar um monte de papel da revista quando isso pode ser evitado). Ainda usando o for com seq:
seq:
for da linguagem C, como veremos a seguir.
for ((var=ini; cond; incr))
do
cmd1
cmd2
cmdn
done
Onde:
var=ini - Significa que a variável var começará de um valor inicial ini;cond - Siginifica que o loop ou laço do for será executado enquanto var não atingir a condição cond;incr - Significa o incremento que a variável var sofrerá em cada passada do loop.
Como sempre vamos aos exemplos que a coisa fica mais fácil:
i partiu do valor inicial 1, o bloco de comando (neste caso somente o echo) será executado enquanto i menor ou igual (<=) a 9 e o incremento de i será de 1 a cada passada do loop.
Repare que no for propriamente dito (e não no bloco de comandos) não coloquei um cifrão ($) antes do i, e a notação para incrementar (i++) é diferente do que vimos até agora. Isto é porque o uso de parênteses duplos (assim como o comando let) chama o interpretador aritmético do Shell, que é mais tolerante.
Como me referi ao comando let, só para mostrar como ele funciona e a versatilidade do for, vamos fazer a mesma coisa, porém omitindo a última parte do escopo do for, passando-a para o bloco de comandos.
for e passou para o bloco de comandos, repare também que quando usei o let, não foi necessário sequer inicializar a variável $i. Veja só os comandos a seguir dados diretamente no prompt para mostrar o que acabo de falar:
$j sequer existia e no primeiro let assumiu o valor 0 (zero) para, após o incremento, ter o valor 1.
Veja só como as coisas ficam simples:
for. Por hoje chega, na próxima vez que nos encontrarmos falaremos sobre outras instruções de loop, mas eu gostaria que até lá você fizesse um pequeno script para contar a quantidade de palavras de um arquivo texto, cujo nome seria recebido por parâmetro.
OBS: Essa contagem tem de ser feita usando o comando for para se habituar ao seu uso. Não vale usar o wc -w.
- Aê Chico! Traz a saideira.
Vou aproveitar também para mandar o meu jabá: diga para os amigos que quem estiver afim de fazer um curso porreta de programação em Shell que mande um e-mail para a nossa gerencia de treinamento para informar-se.
Qualquer dúvida ou falta de companhia para um chope ou até para falar mal dos políticos é só mandar um e-mail para mim.
Valeu!
(CC) 2009 Pelos Frequentadores do Bar do Júlio Neves.