Anda di halaman 1dari 50

Shell Script

Linux está sendo uma revolução para a computação. Ele une liberdade com conhecimento e tecnologia. De uma forma bem
simples envolve e estimula o usuário a conhecer a sua própria máquina e o seu sistema operacional. Uma das formas dessa
interação entre o homem e a máquina é a interface que nela contém, sendo ela o próprio terminal na forma de comandos.

Shell é o nome que se dá à linha de comando em modo texto dos sistemas operacionais Linux e UNIX. E portanto os shell scripts
são um meio de se juntar uma porção de comandos shell em um só arquivo para serem executados quantas vezes forem
necessárias. Os arquivos de lote (bach) do windows são similares, apenas com uma significativa diferença, já que a linha de
comando de sistemas Unix e Linux é mais poderosa.

Lições

• Comece aqui

• Expressões Regulares

• Parte I


o Ambiente Shell;
o Linhas de comando;
o Caracteres de remoção, redirecionamento e ambiente.

• Parte II


o A família grep;
o Passagens de parâmetros.

• Parte III


o Comando cut;
o Comando tr;
o Usando separadores;
o Comando condicionais.

• Parte IV


o Comando test;
o Simplificações dos comandos condicionais.

• Parte V

o Comandos de Loop (ou laço);
o O Comando for;
o Primeira sintaxe do comando for: ;
o Segunda sintaxe do comando for: ;
o Terceira sintaxe do comando for: .

Expressões Regulares

Introdução
Uma Expressão Regular (ER) é um método formal de se especificar um padrão de texto.
É uma composição de símbolos, caracteres com funções especiais, chamados "metacaracteres" que, agrupados entre si e com
caracteres literais, formam uma seqüência, uma expressão. Essa expressão é testada em textos e retorna sucesso caso este
texto obedeça exatamente a todas as suas condições. Diz-se que o texto "casou" com a expressão.
A ERs servem para se dizer algo abrangente de forma específica. Definido o padrão de busca, tem-se uma lista (finita ou não) de
possibilidades de casamento. Em um exemplo rápido, [rgp]ato pode casar "rato", "gato" e "pato".
As ERs são úteis para buscar ou validar textos variáveis como:

• data;
• horário;
• número IP;
• endereço de e-mail;
• endereço de Internet;
• declaração de uma função ();
• dados na coluna N de um texto;
• dados que estão entre <tags></tags>;
• número de telefone, RG, CPF, cartão de crédito.

Vários editores de texto e linguagens de programação têm suporte a ERs, então o tempo investido em seu aprendizado é
recompensado pela larga variedade de aplicativos onde ele pode ser praticado.

Comando grep

Para não precisar listar todo o conteúdo de um arquivo por completo para apenas saber os dados do usuário "root", pode-se usar
o grep para pesquisar e retornar somente a linha dele. O comando grep tem o seguinte formato:
grep palavra arquivo

Vamos utilizar como exemplo o arquivo /etc/passwd, que é a base de usuários de um sistema UNIX/Linux. Vale a pena, antes,
verificar como que se constitui esse arquivo dando o comando:

$cat /etc/passwd

Observa-se que serão obtidas várias linhas, onde cada um se refere a um usuário diferente. E cada linha possui o seguinte
formato:

login : senha : UID : GID : Nome completo : Diretório $HOME : shell

Voltando ao grep.

Para "pescar" somente a linha do usuário root faremos:

aluno@computador:~$ grep root /etc/passwd


root:x:0:0:root:/root:/bin/bash

Os Metacaracteres

Cada metacaracteres é uma ferramenta que tem uma função específica. Servem para dar mais poder às pesquisas, informando
padrões e posições impossíveis de se especificar usando somente caracteres normais.

Os metacaracteres são pequenos pedacinhos simples, que agrupados entre si ou com caracteres normais formam algo maior,
uma expressão. O importante é compreender bem cada um individualmente, e depois apenas lê-los em seqüência.

1. Metacaracteres Representantes

São aqueles cuja função é representar um ou mais caracteres.


Ponto .
Lista [...]
Lista Negada [^...]

O ponto

O ponto é nosso curinga solitário, que está sempre à procura de um casamento não importa com quem seja. Pode ser um
número, uma letra, um TAB, um \@, o que vier ele traça, pois o ponto casa qualquer coisa.
Exemplos:
"n.o" casaria: não, nao, ...
".eclado" casaria: teclado, Teclado, ...
"12.45" casaria: 12:45, 12 45, 12.45, ...

A lista
Bem mais exigente que o ponto, a lista não casa com qualquer um. Ela sabe exatamente o que quer, e nada diferente daquilo, a
lista casa com quem ela conhece. Toda "lista" (os colchetes e seu conteúdo) vale para apenas uma posição, um caractere, por
maior que seja.

Exemplos:
n[ãa]o não, nao, ...
[Tt]eclado teclado, Teclado, ....
12[:. ]45 12:45, 12 45, 12.45, ...

A lista é de certa forma exigente. Sendo assim:


EvitePrefira:
[0123456789] [0-9]
[0-9][0-9]:[0-9][0-9] [012][0-9]:[0-5][0-9]
[A-z] [A-Za-z]

Lista negada
A lista negada é exatamente igual à lista, podendo ter caracteres literais, intervalos e classes POSIX. Tudo o que se aplica a lista
normal, se aplica à negada também.

A única diferença é que ela possui lógica inversa, ou seja, ela casará com qualquer coisa, fora os componentes listados.

Observe que a diferença em sua notação é que o primeiro caractere da lista é um circunflexo, ele indica que esta é uma lista
negada. Então, se [0-9] são números, [^0-9] é qualquer coisa fora números. Pode ser letras, símbolos, espaço em branco,
qualquer coisa menos números. Porém, ao iniciar o circunflexo (^) fora das chaves possui outro significado diferente: simboliza o
início de uma linha.

Mas tem de ser alguma coisa. Só porque ela é uma lista negada isso não significa que ela pode casar "nada".

Exemplos:

[A-Z^] casa maiúsculas e o circunflexo e [^A-Z^] casa tudo fora isso.

Como mandam as regras da boa escrita, sempre após caracteres de pontuação como a vírgula ou o ponto, devemos ter um
espaço em branco os separando do resto do texto.

Então, vamos procurar por qualquer coisa que não o espaço após a pontuação:
[:;,.!?][^ ]

Metacaracteres quantificadores

Os quantificadores servem para indicar o número de repetições permitidas para a entidade imediatamente anterior. Essa
entidade pode ser um caractere ou metacaractere. Em outras palavras, eles dizem a quantidade de repetições que o átomo
anterior pode ter, quantas vezes ele pode aparecer.
São eles:
opcional ?
asterisco *
mais +
chaves {}

Opcional
É útil para procurar palavras no singular e plural e pode ser tornar opcionais caracteres e metacaracteres.
Exemplos:
Expressão Casa com
Ondas? Onda Ondas
Senadora? Senador Senadora
[BFM]?ala ala Bala Fala Mala

Asterisco
Pode aparecer em qualquer quantidade. O curinga .* é o tudo e o nada, qualquer coisa.
Exemplos:
6*0 0, 60, 660, 6660, ..., 666666666660, ...
bi*p bp, bip, biip, biiip, biiiip...
b[ip]* b, bi, bip, biipp, bpipipi, biiiiip ...
Mais

Tem funcionamento idêntico ao do asterisco, tudo o que vale para um, se aplica ao outro. A única diferença é que o mais (+) não
é opcional, então a entidade anterior deve casar pelo menos uma vez, e pode ter várias. Sua utilidade é quando queremos no
mínimo uma repetição.
Exemplos:
6+0 60, 660, 6660, ..., 666666660, ...
bi+p bip, biip, biiip, biiiip...
b[ip]+ bi, bip, biipp, bpipipi, biiiiip, bppp, ...

Chaves

As chaves são a solução para uma quantificação mais controlada, onde se pode especificar exatamente quantas repetições se
quer da entidade anterior.
Colocando um número entre chaves "{ }", indica-se uma quantidade de repetições do caractere (ou metacaractere) anterior. As
chaves são precisas podendo especificar um número exato, um mínimo, um máximo, ou uma faixa numérica. Elas, inclusive,
simulam o *, + e ?.
Exemplos:
{n,m} significa de n até m vezes, assim algo como 6{1,4} casa 6, 66, 666 e 6666. Só, nada mais que isso.
{0,1} zero ou 1 (igual ao opcional)
{0,} zero ou mais (igual ao asterisco)
{1,} um ou mais (igual ao mais)
{3} exatamente

Metacaracteres tipo âncora

São aqueles que não casam caracteres ou definem quantidades, ao invés disso eles marcam uma posição específica na linha.
Assim, eles não podem ser quantificados, então o mais, o asterisco e as chaves não têm influência sobre âncoras:

São eles:

• cincunflexo - ^
• cifrão - $
• borda - /b

Explicando cada metacaractere

1. Circunflexo - ^

Este metacaractere (do tipo de posicionamento por representar uma posição específica da linha) simboliza o início de uma linha.
É também o marcador de lista negada, mas apenas dentro da lista (e no começo), fora dela ele é a âncora que marca o início de
uma linha, veja:

^[0-9] significa que casa com uma linha começando com qualquer algarismo. O inverso disso seria: ^[^0-9]

2. Cifrão - o fim $

Este é similar e complementar ao circunflexo, pois representa o fim de uma linha e só é válido no final de uma expressão
regular.

Quando demos o comando:

$ grep bash$ /etc/passwd

significa que procuramos pela palavra "bash" no final da linha, ou ainda, a palavra "bash" seguida de um fim de linha.

Esse cifrão é o mesmo caractere que é utilizado para identificar as variáveis do shell, como $PWD e $HOME. Para evitar
possíveis problemas com a expansão de variáveis, é preciso "proteger" a expressão regular passada ao grep. A proteção é
feita colocando-se a ER entre 'aspas simples' fazendo:
$ grep 'bash$' /etc/passwd

Veja outros exemplos:


[0-9]$ - casa linhas que terminam com um número
^$ - casa com linhas vazias
^.{20,60}$ - casa com linhas que têm entre 20 e 60 caracteres

3. Borda - a limítrofe \b
A borda marca os limites de uma palavra, ou seja, onde ela começa e/ou termina. É muito útil para casar palavras exatas, e não
partes de palavras. Palavra aqui é um conceito que engloba [A-Za-z0-9_] apenas, ou seja, letras, números e o sublinhado.

Veja os exemplos:
Veja como se comportam as ERs nas palavras dia, diafragma, radial, melodia e bom-dia!:
dia --- dia, diafragma, radial, melodia, bom-dia!
\bdia --- dia, diafragma, bom-dia!
dia\b --- dia, melodia, bom-dia!
\bdia\b --- dia, bom-dia!

Outros metacaracteres

Vamos ver outros metacaracteres, que têm funções específicas e não relacionadas entre si, portanto não podem ser agrupados
em outra classe fora a tradicional "outros". Mas atenção, isso não quer dizer que eles são inferiores, pelo contrário, o poder das
ERs é multiplicado com seu uso e um mundo de possibilidades novas se abre a sua frente.

São eles:

• escape \
• ou |
• grupo ()
• retrovisor /n

Explicando-os melhor...

1. Escape - a criptonita \

Temos duas formas de casar um metacaractere dentro de uma ER:

• Usando Listas: Lua[*] casa com Lua*


• "Escapando" o Caractere: Lua\* casa com Lua*

Isto é, a contrabarra (\) "escapa" qualquer metacaractere, tirando todos os seus poderes. O escape é tão poderoso que
pode escapar a si próprio! O \ casa uma barra invertida \ literal. Então, agora que sabemos muito sobre ERs, que tal uma
expressão para casar um número de RG? Lembre que ele tem o formato n.nnn.nnn-n, é fácil!

[0-9]\.[0-9]{3}\.[0-9]{3}-[0-9]

O \* = [*] = asterisco literal

Ironia -> O escape escapa o escape, escapando-se a si próprio simultaneamente.

2. Ou - o alternativo |

Para procurar por uma coisa ou outra, deve-se usar o pipe "|" e delimitar as opções com os parênteses " ". É muito
comum em uma posição específica de nossa Expressão Regular (ER) termos mais de uma alternativa possível, por
exemplo, ao casar um cumprimento amistoso, podemos ter uma terminação diferente para cada parte do dia:

boa-tarde|boa-noite

O 'ou' serve para esses casos em que precisamos dessas alternativas. Essa ER se lê: "ou boa-tarde, ou boa-noite", ou
seja "ou isso ou aquilo". Lembre que a lista também é uma espécie de ou (|), mas apenas para uma letra, então:
[gpr]ato é o mesmo que gato|pato|rato

São similares, embora nesse caso em que apenas uma letra muda entre as alternativas, a lista é a melhor escolha. Em
outro exemplo, o ou é útil também para casarmos um endereço de Internet, que pode ser uma página, ou um sítio FTP

http://|ftp://

3. Grupo - o pop (...)

Assim como artistas famosos e personalidades que conseguem arrastar multidões, o grupo tem o dom de juntar vários
tipos de sujeitos em um mesmo local. Dentro de um grupo podemos ter um ou mais caracteres, metacarateres e inclusive
outros grupos! Como em uma expressão matemática, os parênteses definem um grupo, e seu conteúdo pode ser visto
como um bloco na expressão.

Todos os metacaracteres quantificadores que vimos anteriormente, podem ter seu poder ampliado pelo grupo, pois ele
lhes dá mais abrangência. E o 'ou', pelo contrário, tem sua abrangência limitada pelo grupo, e pode parecer estranho,
mas é essa limitação que lhe dá mais poder.

Em um exemplo simples,

(ai)+ agrupa a palavra ai e esse grupo está quantificado pelo mais (+). Isso quer dizer que casamos várias repetições da
palavra, como ai, aiai, aiaiai, ... E assim podemos agrupar tudo o que quisermos, literais e metacaracteres, e quantificá-los:

(ha!)+ ha!, ha!ha!, ha!ha!ha!, ...

(\.[0-9]){3} .0.6.2, .2.8.9, .6.6.6, ...

(www\.)?zz\.com www.zz.com, zz.com

E em especial nosso amigo ou ganha limites ou seu poder cresce:

boa-(tarde|noite) boa-tarde, boa-noite

(#|n\.|núm) 6 # 6, n. 6, núm 6

(in|con)?certo incerto, concerto, certo

Podemos criar subgrupos também, então imagine que você esteja procurando o nome de um supermercado em uma listagem e
não sabe se este é um mercado, supermercado ou um hipermercado.

(super|hiper)mercado

Consegue casar as duas últimas possibilidades, mas note que nas alternativas super e hiper temos um trecho per comum
aos dois, então podíamos "alternativizar" apenas as diferenças su e hi:

(su|hi)permercado

Precisamos também casar apenas o mercado sem os aumentativos, então temos de agrupá-los e torná-los opcionais:

((su|hi)per)?mercado

Ei! E se tivesse minimercado também?

(mini|(su|hi)per)?mercado

4. Retrovisor - o saudosista \1 ... \9

(quero)-\1

Mas esse \1 não é o tal do escape?

Pois é, lembra que o escape (\) servia para tirar os poderes do metacaractere seguinte. Então, a essa definição agora
incluímos: a não ser que este próximo caractere seja um número de 1 a 9, então estamos lidando com um retrovisor.
Notou o detalhe? Podemos ter no máximo 9 retrovisores por ER, então \10 é o retrovisor número 1 seguido de um zero.

O verdadeiro poder do retrovisor é quando não sabemos exatamente qual texto o grupo casará. Vamos estender o quero do
exemplo anterior para "qualquer palavra":

([A-Za-z]+)-\1

Viu o poder dessa ER? Ela casa palavras repetidas, separadas por um traço, como o próprio quero-quero, e mais: bate-
bate, come-come, etc. Mas, e se tornássemos o traço opcional?

([A-Za-z]+)-?\1

Com uma modificação pequena, fazemos um minicorretor ortográfico para procurar por palavras repetidas como estas em
um texto:

([A-Za-z]+) \1

Mas lembre-se que procuramos por palavras inteiras e não apenas trechos delas, então precisamos usar as bordas para
completar nossa ER:

\b([A-Za-z]+) \1\b

Como já dito, podemos usar no máximo nove retrovisores. Vamos ver uns exemplos com mais de um de nossos amigos novos:

o (lenta)(mente) é \2 \1 lentamente é mente lenta


o ((band)eira)nte \1 \2a bandeirante bandeira banda
o in(d)ol(or) é sem \1\2 indolor é sem dor
o ((((a)b)c)d)-1 = \1,\2,\3,\4 abcd-1 = abcd,abc,ab,a

Repare que numeram-se retrovisores contando os grupos da esquerda para a direita.

Repare que numeram-se retrovisores contando os grupos da esquerda para a direita.

Ao usar um (grupo) qualquer, você ganha um brinde, e muitas vezes nem sabe.
O brinde é o trecho de texto casado pela ER que está no grupo, que fica guardado em um cantinho especial e pode ser usado em
outras partes da mesma ER.

Então, o retrovisor \1é uma referência ao texto casado do primeiro grupo, nesse caso quero, ficando, no fim das contas, a
expressão que queríamos. O retrovisor pode ser lembrado também como um link ou um ladrão, pois copia o texto do grupo.

Como o nome diz, é retrovisor porque ele "olha pra trás", para buscar um trecho já casado. Isso é muito útil para casar trechos
repetidos em uma mesma linha. Veja bem, é o texto, e não a ER.

Como exemplo, em um texto, procuramos quero-quero. Podemos procurar literalmente por quero-quero, mas assim não tem
graça, vamos usar o grupo e o retrovisor para fazer isso

Parte I

Diálogo entre ouvido um Linuxer e um empurrador de mouse:

- Quem é o Bash?

- O Bash é o filho mais novo da família Shell.

- Pô cara! Estás a fim de me deixar maluco? Eu tinha uma dúvida e você me deixa com duas!

- Não, maluco você já é há muito tempo. Desde que se decidiu a usar aquele sistema operacional que você tem que dar dez
boots por dia e não tem domínio nenhum sobre o que está acontecendo no seu computador. Mas deixa isso prá lá, vou te
explicar o que é Shell e os componentes de sua família e ao final da explanação você dirá: "Meu Deus do Shell! Porque eu não
optei pelo Linux antes?".
O ambiente Linux

Para você entender o que é e como funciona o Shell, primeiro será mostrado como funciona o ambiente em camadas do Linux.
Dê uma olhada no gráfico abaixo:

Neste gráfico dá para ver que a camada de hardware é a mais profunda e é formada pelos componentes físicos do seu
computador. Envolvendo esta, vem a camada do kernel que é o cerne do Linux, seu núcleo, é quem coloca o hardware
para funcionar fazendo seu gerenciamento e controle. Os programas e comandos que envolvem o kernel, dele se utilizam
para realizar as tarefas aplicativas para que foram desenvolvidos. Fechando tudo isso vem o Shell que leva este nome
porque em inglês, Shell significa concha, carapaça, isto é, fica entre o usuário e o sistema operacional, de forma que tudo
que interage com o sistema operacional, tem que passar pelo seu crivo.

O ambiente Shell

Bom já que para chegar ao núcleo do Linux, no seu kernel que é o que interessa a todo aplicativo, é necessária a filtragem do
Shell, vamos entender como ele funciona de forma a tirar o máximo proveito das inúmeras facilidades que ele nos oferece.

O Linux por definição é um sistema multiusuário - não podemos nunca esquecer disto - e para permitir o acesso de determinados
usuários e barrar a entrada de outros, existe um arquivo chamado /etc/passwd que além de fornecer dados para esta função de
"leão-de-chácara" do Linux, também provê informações para o login daqueles que passaram por esta primeira barreira. O último
campo de seus registros informa ao sistema qual Shell a pessoa receberá ao se "logar" (ARGH!!!).

Lembra que foi falado de Shell, família, irmão? Pois é, vamos começar a entender isto: o Shell, que se vale da imagem de uma
concha envolvendo o sistema operacional propriamente dito, é o nome genérico para tratar os filhos desta idéia que, ao longo
dos anos de existência do sistema operacional Unix foram aparecendo. Atualmente existem diversos sabores de Shell, dentre
estes é destacado o sh (Bourne Shell), o ksh (Korn Shell), bash (Bourne Again Shell) e o csh (C Shell).

Uma rapidinha nos principais sabores de Shell

Bourne Shell (sh)

Desenvolvido por Stephen Bourne da Bell Labs (da AT&T onde também foi desenvolvido o Unix), este foi durante muitos
anos o Shell default do sistema operacional Unix. É também chamado de Standard Shell por ter sido durante vários anos
o único e até hoje é o mais utilizado até porque ele foi portado para todos os ambientes Unix e distros Linux.
Korn Shell (ksh)

Desenvolvido por David Korn, também da Bell Labs, é um superset do sh, isto é, possui todas as facilidades do sh e a elas
agregou muitas outras. A compatibilidade total com o sh vem trazendo muitos usuários e programadores de Shell para
este ambiente.

Bourne Again Shell (bash)

Este é o Shell mais moderno e cujo número de adeptos mais cresce em todo o mundo, seja por ser o Shell default do
Linux, seu sistema operacional hospedeiro, seja por sua grande diversidade de comandos, que incorpora inclusive diversos
instruções características do C Shell.

C Shell (csh)

Desenvolvido por Bill Joy da Berkley University é o Shell mais utilizado em ambientes *BSD e Xenix. A estruturação de
seus comandos é bem similar à da linguagem C. Seu grande pecado foi ignorar a compatibilidade com o sh, partindo por
um caminho próprio.

Além destes Shells existem outros, mas irei falar contigo somente sobre os três primeiros, tratando-os genericamente por
Shell e assinalando as especificidades de cada um que porventura hajam.

Quando eu disse que o último campo do /etc/passwd informa ao sistema qual é o Shell que o usuário vai receber
ao se "logar", é para ser interpretado ao pé-da-letra, isto é, se neste campo do seu registro estiver prog, a pessoa
ao acessar o sistema receberá a tela de execução do programa prog e ao terminar a sua execução ganhará
imediatamente um logout. Imagine o quanto se pode incrementar a segurança com este simples artifício.

Explicando o funcionamento do Shell

O Shell é o primeiro programa que você ganha ao se "logar" no Linux. É ele que resolverá várias coisas de forma a não onerar o
kernel com tarefas repetitivas, aliviando-o para tratar assuntos mais nobres. Como cada usuário possui o seu próprio Shell
interpondo-se entre ele e o Linux, é o Shell quem interpreta os comandos que são teclados e examina as suas sintaxes,
passando-os esmiuçados para execução.

- Êpa! Esse negócio de interpretar comando não tem nada a haver com interpretador não, né?

- Tem sim, na verdade o Shell é um interpretador (ou será intérprete) que traz consigo uma poderosa linguagem com
comandos de alto nível, que permite construção de loops (laços), de tomadas de decisão e de armazenamento de valores
em variáveis, como vou te mostrar.

Vou te explicar as principais tarefas que o Shell cumpre, na sua ordem de execução. Preste atenção nesta ordem porque
ela é fundamental para o entendimento do resto do nosso bate papo.

Exame da Linha de Comandos

Neste exame, o Shell identifica os caracteres especiais (reservados) que têm significado para interpretação da linha, logo
após verifica se a linha passada é um comando ou uma atribuição.

Comando

Quando uma linha é digitada no prompt do Linux, ela é dividida em pedaços separados por espaço em branco: o primeiro
pedaço é o nome do programa que terá sua existência pesquisada; identifica em seguida, nesta ordem,
opções/parâmetros, redirecionamentos e variáveis.

Quando o programa identificado existe, o Shell verifica as permissões dos arquivos envolvidos (inclusive o próprio
programa), dando um erro caso você não esteja credenciado a executar esta tarefa.

Atribuição

Se o Shell encontra dois campos separados por um sinal de igual (=) sem espaços em branco entre eles, identifica
esta seqüência como uma atribuição.
Exemplos

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-top: 1em; margin-bottom:
1em; margin-right: 0pt; text-align: justify; font-weight: bold; color: rgb(0, 0, 0);">$ ls linux
linux

Neste exemplo o Shell identificou o ls como um programa e o linux como um parâmetro passado para o programa ls.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-top: 1em; margin-bottom:
1em; margin-right: 0pt; text-align: justify; color: rgb(0, 0, 0);">$ valor=1000

Neste caso, por não haver espaços em branco (já dá para notar que o branco é um dos caracteres reservados) o Shell
identificou uma atribuição e colocou 1000 na variável valor.

Jamais Faça:
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-
background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy:
-moz-initial;">$ valor = 1000
bash: valor: not found

Neste caso, o Bash achou a palavra valor isolada por brancos e julgou que você estivesse mandando
executar um programa chamado valor, para o qual estaria passando dois parâmetros: = e 1000.

Resolução de Redirecionamentos

Após identificar os componentes da linha que você teclou, o Shell parte para a resolução de redirecionamentos. O Shell
tem incorporado ao seu elenco de vantagens o que chamamos de redirecionamento, que pode ser de entrada (stdin), de
saída (stdout) ou dos erros (stderr), conforme será explicado a seguir.

Substituição de Variáveis

Neste ponto, o Shell verifica se as eventuais variáveis (parâmetros começados por $), encontradas no escopo do
comando, estão definidas e as substitui por seus valores atuais

Substituição de Meta Caracteres

Se algum metacaractere (*, ? ou []) foi encontrado na linha de comando, neste ponto ele será substituído por seus
possíveis valores. Supondo que o único arquivo no seu diretório corrente começado pela letra n seja um diretório
chamado "nomegrandeprachuchu", se você fizer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-top: 1em; margin-bottom:
1em; margin-right: 0pt; text-align: justify;">$ cd n*

Passa Linha de Comando para o kernel

Completadas as tarefas anteriores, o Shell monta a linha de comandos, já com todas as substituições feitas, chama o
kernel para executá-la em um novo Shell (Shell filho), ganhando um número de processo (PID ou Process Identification) e
permanece inativo durante a execução do programa. Uma vez encerrado este processo (juntamente com o Shell filho),
recebe novamente o controle e, exibindo um prompt, mostra que está pronto para executar outros comandos.

Decifrando a Pedra da Roseta

Para tirar aquela sensação que você tem quando vê um script Shell, que mais parece uma sopa de letrinhas ou um hieróglifo vou
lhe mostrar os principais caracteres especiais para que você saia por ai como o Jean-François Champollion decifrando a Pedra
da Roseta (dê uma googlada para descobrir quem é este cara, acho que vale a pena).

Caracteres para remoção do significado

É isso mesmo, quando não se deseja que o Shell interprete um caractere especial, deve-se "escondê-lo" dele. Isso pode
ser feito de três formas distintas:

Apóstrofo ou plic (')


Quando o Shell vê uma cadeia de caracteres entre apóstrofos ('), ele tira os apóstrofos da cadeia e não interpreta seu
conteúdo.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;"> $ ls linux*
linuxmagazine
$ ls 'linux*'
bash: linux* no such file or directory

No primeiro caso o Shell "expandiu" o asterisco e descobriu o arquivo linuxmagazine para listar. No segundo, os
apóstrofos inibiram a interpretação do Shell e veio a resposta que não existe o arquivo linux*.

Contrabarra ou Barra Invertida (\)

IIdêntico aos apóstrofos exceto que a barra invertida inibe a interpretação somente do caractere que a segue.

Suponha que você, acidentalmente, tenha criado um arquivo chamado * (asterisco) - que alguns sabores de Unix
permitem - e deseja removê-lo. Se você fizesse:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(255, 255, 0); text-align:
justify;"> $ rm *

Você estaria fazendo a maior encrenca, pois o rm removeria todos os arquivos do diretório corrente. A melhor forma de
fazer o pretendido é:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;"> $ rm \*

Desta forma, o Shell não interpretaria o asterisco, e em conseqüência não faria a sua expansão.

Faça a seguinte experiência científica:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(255, 255, 0); text-align:
justify;"> $ cd /etc
$ echo '*'
$ echo \*
$ echo *

Viu a diferença? Então não precisa explicar mais.

Aspas (")

Exatamente igual ao apóstrofo exceto que, se a cadeia entre aspas contiver um cifrão ($), uma crase (`), ou uma barra
invertida (\), estes caracteres serão interpretados pelo Shell.

Não precisa se estressar, eu não te dei exemplos do uso das aspas por que você ainda não conhece o cifrão ( $) nem a
crase (`). Daqui para frente veremos com muita constância o uso destes caracteres especiais, o mais importante é
entender o significado de cada um.

Caracteres de redirecionamento

A maioria dos comandos tem uma entrada, uma saída e pode gerar erros. Esta entrada é chamada Entrada Padrão ou
stdin e seu default é o teclado do terminal. Analogamente, a saída do comando é chamada Saída Padrão ou stdout e seu
default é a tela do terminal. Para a tela também são enviadas por default as mensagens de erro oriundas do comando que
neste caso é a chamada Saída de Erro Padrão ou stderr. Veremos agora como alterar este estado de coisas.

Vamos fazer um programa gago. Para isto faça:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ cat

O cat é uma instrução que lista o conteúdo do arquivo especificado para a Saída Padrão (stdout). Caso a entrada não seja
definida, ele espera os dados da stdin. Como não foi especificada a entrada, ele está esperando-a pelo teclado (Entrada
Padrão) e como também não foi citada a saída, o que será teclado irá para a tela (Saída Padrão) fazendo desta forma, um
programa gago. Experimente!

Redirecionamento da Saída Padrão

Para especificarmos a saída de um programa usamos o > (maior que) ou o >> (maior, maior) seguido do nome do arquivo
para o qual se deseja mandar a saída.

Vamos transformar o programa gago em um editor de textos (que pretensão heim!).


http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ cat > Arq

O cat continua sem ter a entrada especificada, portanto está aguardando que os dados sejam teclados, porém a sua saída
está sendo desviada para o arquivo Arq. Assim sendo, tudo que esta sendo teclado esta indo para dentro de Arq, de forma
que fizemos o editor de textos mais curto e ruim do planeta.

Se eu fizer novamente:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ cat > Arq

Os dados contidos em Arq serão perdidos, já que antes do redirecionamento o ShellArq estava vazio. Para colocar mais
informações no final do arquivo eu deveria ter feito: criará um

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ cat >> Arq

Como já haviamos lhe dito, o Shell resolve a linha e depois manda o comando para a execução. Assim, se você
redirecionar a saída de um arquivo para ele próprio, primeiramente o Shell "esvazia" este arquivo e depois manda
o comando para execução, desta forma você acabou de perder o conteúdo do seu arquivo. Com isso dá para notar
que o >> (maior maior) serve para inserir texto no final do arquivo.

Com isso dá para notar que o >> (maior maior) serve para inserir texto no final do arquivo.

Redirecionamento da Saída de Erro Padrão

Assim como o default do Shell é receber os dados do teclado e mandar as saídas para a tela, os erros também serão
enviados para a tela se você não especificar para onde deverão ser enviados. Para redirecionar os erros use 2>
SaidaDeErro ?. Note que entre o número 2 e o sinal de maior (>) não existe espaço em branco.

Preste atenção! Não confunda >> com 2>. O primeiro anexa dados ao final de um arquivo, e o segundo
redireciona a Saída de Erro Padrão (stderr) para um arquivo que está sendo designado. Isso é importante!

Suponha que durante a execução de um script você pode, ou não (dependendo do rumo tomado pela execução do
programa), ter criado um arquivo chamado /tmp/seraqueexiste$$. Para não ficar sujeira no seu disco, ao final do script
você colocaria uma linha:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ rm
/tmp/seraqueexiste$ $ (dois cifrões juntos)

Caso o arquivo não existisse seria enviado para a tela uma mensagem de erro. Para que isso não aconteça deve-se fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ rm
/tmp/seraqueexiste$ $ 2> /dev/null (dois cifrões juntos)

Sobre o exemplo que acabamos de ver tenho duas dicas a dar:


Dica # 1

O $ $ (dois cifrões juntos) contêm o PID, isto é, o número do seu processo. Como o Linux é
multiusuário, é bom anexar sempre o $ $(dois cifrões juntos) ao nome dos arquivos que serão
usados por várias pessoas para não haver problema de propriedade, isto é, caso você batizasse o
seu arquivo simplesmente como seraqueexiste, o primeiro que o usasse (criando-o então) seria o
seu dono e todos os outros ganhariam um erro quando tentassem gravar algo nele.

Para que você teste a Saída de Erro Padrão direto no prompt do seu Shell, vou dar mais um exemplo. Faça:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;">$ ls naoexiste
bash: naoexiste no such file or directory
$ ls naoexiste 2> arquivodeerros
$
$ cat arquivodeerros
bash: naoexiste no such file or directory

Neste exemplo, vimos que quando fizemos um ls em naoexiste, ganhamos uma mensagem de erro. Após, redirecionarmos
a Saída de Erro Padrão para arquivodeerros e executarmos o mesmo comando, recebemos somente o prompt na tela.
Quando listamos o conteúdo do arquivo para o qual foi redirecionada a Saída de Erro Padrão, vimos que a mensagem de
erro tinha sido armazenada nele. Faça este teste ai.

Dica # 2

- Quem é esse tal de /dev/null?

- Em Unix existe um arquivo fantasma. Chama-se /dev/null. Tudo que é mandado para este arquivo
some. Assemelha-se a um Buraco Negro. No caso do exemplo, como não me interessava guardar a
possível mensagem de erro oriunda do comando rm, redirecionei-a para este arquivo.

É interessante notar que estes caracteres de redirecionamento são cumulativos, isto é, se no exemplo anterior
fizéssemos:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ ls naoexiste
2>> arquivodeerros

a mensagem de erro oriunda do ls seria anexada ao final de arquivodeerros.

Redirecionamento da Entrada Padrão

Para fazermos o redirecionamento da Entrada Padrão usamos o < (menor que).

- E prá que serve isso? - você vai me perguntar.

- Deixa eu te dar um exemplo que você vai entender rapidinho.

Suponha que você queira mandar um mail para o seu chefe. Para o chefe nós caprichamos, né? então ao invés de sair
redigindo o mail direto no prompt da tela de forma a tornar impossível a correção de uma frase anterior onde, sem
querer, escreveu um "nós vai", você edita um arquivo com o conteúdo da mensagem e após umas quinze verificações sem
constatar nenhum erro, decide enviá-lo e para tal faz:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ mail chefe <
arquivocommailparaochefe

O teu chefe então receberá o conteúdo do arquivocommailparaochefe.

Um outro tipo de redirecionamento muito louco que o Shell te permite é o chamado here document. Ele é representado
por << (menor menor) e serve para indicar ao Shell que o escopo de um comando começa na linha seguinte e termina
quando encontra uma linha cujo conteúdo seja unicamente o label que segue o sinal <<.

Veja o fragmento de script a seguir, com uma rotina de ftp:


http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(255, 255, 0); text-align:
justify;">$ ftp –ivn hostremoto << fimftp
user $Usuário $Senha
binary
get arquivoremoto
fimftp

Neste pedacinho de programa temos um monte de detalhes interessantes:

1. As opções que usei para o ftp (-ivn) servem para ele ir listando tudo que está acontecendo (—v de verbose), para
não perguntar se você tem certeza de que deseja transmitir cada arquivo (—i de interactive), e finalmente a
opção —n serve para dizer ao ftp para ele não solicitar o usuário e sua senha, pois esses serão informados pela
instrução específica (user);
2. Quando eu usei o << fimftp, estava dizendo o seguinte para o intérprete: "Olhe aqui Shell, não se meta em nada
a partir daqui até encontrar o label fimftp. Você não entenderia nada, já que são instruções específicas do
comando ftp e você não entende nada de =ftp=".

Se fosse só isso seria simples, mas pelo próprio exemplo dá para ver que existem duas variáveis ($Usuário e $Senha), que o
Shell vai resolver antes do redirecionamento. Mas a grande vantagem desse tipo de construção é que ela permite que comandos
também sejam interpretados dentro do escopo do here document, o que também contraria o que acabei de dizer. Logo a seguir
explico como esse negócio funciona. Agora ainda não dá, está faltando ferramenta.

1. O comando user é do repertório de instruções do ftp e serve para passar o usuário e a senha que haviam sido
lidos em uma rotina anterior a esse fragmento de código e colocados respectivamente nas duas variáveis:
$Usuário e $Senha.
2. O binary é outra instrução do ftp, que serve para indicar que a transferência de arquivoremoto será feita em
modo binário, isto é, o conteúdo do arquivo não será interpretado para saber se está em ASCII, EBCDIC, ...
3. O get arquivoremoto diz ao ftp para pegar esse arquivo em hostremoto e trazê-lo para o nosso host local. Se
fosse para mandar o arquivo, usaríamos o comando put.

Um erro muito freqüente no uso de labels (como o fimftp do exemplo anterior) é causado pela presença de
espaços em branco antes ou após o mesmo. Fique muito atento quanto a isso, por que este tipo de erro costuma
dar uma boa surra no programador, até que seja detectado. Lembre-se: um label que se preze tem que ter uma
linha inteira só para ele.

- Está bem, está bem! Eu sei que dei uma viajada e entrei pelos comandos do ftp, fugindo ao nosso assunto que é Shell,
mas como é sempre bom aprender e é raro as pessoas estarem disponíveis para ensinar...

Redirecionamento de Comandos

Os redirecionamentos que falamos até aqui sempre se referiam a arquivos, isto é mandavam para arquivo, recebiam de
arquivo, simulavam arquivo local, ... O que veremos a partir de agora redireciona a saída de um comando para a entrada
de outro. É utilíssimo e quebra os maiores galhos. Seu nome é pipe (que em inglês significa tubo, já que ele encana a
saída de um comando para a entrada de outro) e sua representação é uma barra vertical (|).

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;"> $ ls | wc -l
21

O comando ls passou a lista de arquivos para o comando wc, que quando está com a opção –l conta a quantidade de
linhas que recebeu. Desta forma, podemos afirmar categoricamente que no meu diretório existiam 21 arquivos.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;"> $ cat
/etc/passwd |sort | lp

Esta linha de comandos manda a listagem do arquivo /etc/passwd para a entrada do comando sort. Este a classifica e
manda-a para o lp que é o gerenciador do spool de impressão.
Caracteres de Ambiente

Quando quer priorizar uma expressão você coloca-a entre parênteses não é? Pois é, por causa da aritmética é normal
pensarmos deste jeito. Mas em Shell o que prioriza mesmo são as crases (`) e não os parênteses. Vou dar exemplos de
uso das crases para você entender melhor.

Eu quero saber quantos usuários estão "logados" no computador que eu administro. Eu posso fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; font-weight: bold;
color: rgb(0, 0, 0);">$ who | wc -l
8

O comando who passa a lista de usuários conectados para o comando wc –l que conta quantas linhas recebeu e lista a
resposta na tela. Pois bem, mas ao invés de ter um oito solto na tela, o que eu quero é que ele esteja no meio de uma
frase.

Ora para mandar frases para a tela eu uso o comando echo, então vamos ver como é que fica:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;">$ echo "Existem who | wc -l usuários conectados"
Existem who | wc -l usuários conectados

Hi! Olha só, não funcionou! É mesmo, não funcionou e não foi por causa das aspas que eu coloquei, mas sim por que eu
teria que ter executado o who | wc -l antes do echo. Para resolver este problema, tenho que priorizar esta segunda parte
do comando com o uso de crases, fazendo assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; font-weight: bold;
color: rgb(0, 0, 0);">$ echo "Existem `who | wc -l` usuários conectados"
Existem 8 usuários conectados

Para eliminar esse monte de brancos antes do 8 que o wc -l produziu, basta tirar as aspas. Assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; font-weight: bold;
color: rgb(0, 0, 0);">$ echo Existem `who | wc -l` usuários conectados
Existem 8 usuários conectados

Como eu disse antes, as aspas protegem tudo que está dentro dos seus limites, da interpretação do Shell. Como para o
Shell basta um espaço em branco como separador, o monte de espaços será trocado por um único após a retirada das
aspas.

Antes de falar sobre o uso dos parênteses deixa eu mandar uma rapidinha sobre o uso de ponto-e-vírgula ( ;). Quando
estiver no Shell, você deve sempre dar um comando em cada linha. Para agrupar comandos em uma mesma linha
teremos que separá-los por ponto-e-vírgula. Então:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; font-weight: bold;
color: rgb(0, 0, 0);">$pwd ; cd /etc; pwd; cd -; pwd
/home/meudir
/etc/
/home/meudir

Neste exemplo, listei o nome do diretório corrente com o comando pwd, mudei para o diretório /etc, novamente listei o
nome do diretório e finalmente voltei para o diretório onde estava anteriormente (cd -), listando seu nome. Repare que
coloquei o ponto-e-vírgula (;) de todas as formas possíveis para mostrar que não importa se existe espaços em branco
antes ou após este caractere.

Finalmente vamos ver o caso dos parênteses. Veja só o caso a seguir, bem parecido com o exemplo anterior:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ (pwd ; cd /etc ; pwd;)
/home/meudir
/etc/
$ pwd
/home/meudir

- Quequeiiisso minha gente? Eu estava no /home/meudir, mudei para o /etc, constatei que estava neste diretório com o
pwd seguinte e quando o agrupamento de comandos terminou, eu vi que continuava no /etc/meudir, como se eu nunca
houvesse saído de lá!

- Ih! Será que é tem coisa de mágico aí?

- Tá me estranhando, rapaz? Não é nada disso! O interessante do uso de parênteses é que ele invoca um novo Shell para
executar os comandos que estão no seu interior. Desta forma, realmente fomos para o diretório /etc, porém quando todos
os comandos dentro dos parênteses foram executados, o novo Shell que estava no diretório /etcShell anterior cujo
diretório corrente era /home/meudir. Faça outros testes usando cd, e ls para você firmar o conceito.

Agora que já conhecemos estes conceitos veja só este exemplo a seguir:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ mail suporte
<< FIM
>Ola suporte, hoje as ‘date"+%hh:mm"‘
>ocorreu novamente aquele problema
>que eu havia reportado por
>telefone. Conforme seu pedido
>ai vai uma listagem dos arquivos
>do diretorio:
>‘ls —l‘
>Abracos a todos.
>FIM

Finalmente agora temos conhecimento para mostrar o que havíamos conversado sobre here document. Os comandos
entre crases (`) serão priorizados e portanto o Shell os executará antes da instrução mail. Quando o suporte receber o e-
mail, verá que os comandos date e ls foram executados imediatamente antes do comando mail, recebendo então uma
fotografia do ambiente no momento em que a correspondência foi enviada.

O prompt primário default do Shell, como vimos, é o cifrão ($), porém o Shell usa o conceito de prompt secundário, ou de
continuação de comando, que é enviado para a tela quando há uma quebra de linha e a instrução não terminou. Esse
prompt, é representado por um sinal de maior (>), que vemos precedendo a partir da 2ª linha do exemplo.

Para finalizar e bagunçar tudo, devo dizer que existe uma construção mais moderna que vem sendo utilizada como forma
de priorização de execução de comandos, tal qual as crases (`). São as construções do tipo $(cmd), onde cmd é um (ou
vários) comando que será(ão) executado(s) com prioridade em seu contexto.

Assim sendo, o uso de crases (`) ou construções do tipo $(cmd) servem para o mesmo fim, porém para quem trabalha
com sistemas operacionais de diversos fornecedores (multiplataforma), aconselho o uso das crases, já que o $(cmd) não
foi portado para todos os sabores de Shell. Aqui dentro do Botequim, usarei ambas as formas, indistintamente.

Vejamos novamente o exemplo dado para as crases sob esta nova ótica:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;">$ echo Existem $(who | grep wc -l) usuários conectados
Existem 8 usuários conectados

Veja só este caso:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; font-weight: bold;
color: rgb(0, 0, 0);">$ Arqs=ls
$ echo $Arqs
ls

Neste exemplo eu fiz uma atribuição (=) e executei uma instrução. O que eu queria era que a variável $Arqs, recebesse a
saída do comando ls. Como as instruções de um script são interpretadas de cima para baixo e da esquerda para a direita,
a atribuição foi feita antes da execução do ls. Para fazer o que desejamos é necessário que eu priorize a execução deste
comando em detrimento da atribuição e isto pode ser feito de qualquer uma das maneiras a seguir:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ Arqs='ls'
ou:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ Arqs=$(ls)

Para encerrar este assunto vamos ver só mais um exemplo. Digamos que eu queira colocar dentro da variável $Arqs a
listagem longa (ls -l) de todos os arquivos começados por arq e seguidos de um único caractere (?). Eu deveria fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ Arqs=$(ls -l
arq?)

ou:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ Arqs=`ls -l
arq?`

Mas veja:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ echo $Arqs
-rw-r--r--
1 jneves jneves 19 May 24 19:41 arq1 -rw-r--r-- 1 jneves jneves 23 May
24 19:43 arq2 -rw-r--r-- 1 jneves jneves 1866 Jan 22 2003 arql

- Pô, saiu tudo embolado!

- Pois é cara, como eu já te disse, se você deixar o Shell “ver” os espaços em branco, sempre que houver diversos
espaços juntos, eles serão trocados por apenas um. Para que a listagem saia bonitinha, é necessário proteger a variável
da interpretação do Shell, assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ echo "$Arqs"
-rw-r--r-- 1 jneves jneves 19 May 24 19:41 arq1
-rw-r--r-- 1 jneves jneves 23 May 24 19:43 arq2
-rw-r--r-- 1 jneves jneves 1866 Jan 22 2003 arql

- Olhe, amigo, vá treinando esses exemplos, porque, quando nos encontrarmos novamente, vou lhe explicar uma série de
instruções típicas de programação Shell. Tchau! Ahh! Só mais uma coisinha que eu ia esquecendo de lhe dizer. Em Shell, o
"jogo da velha" (#) é usado quando desejamos fazer um comentário.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ exit # pede a
conta ao garcon

Parte II

Diálogo

- Garçom! Traz um "chops" e dois "pastel". O meu amigo hoje não vai beber por que ele finalmente esta sendo apresentado a um
verdadeiro sistema operacional e ainda tem muita coisa a aprender!
- E então, amigo, tá entendendo tudo que te expliquei até agora?
- Entendendo eu tô, mas não vi nada prático nisso...
- Calma rapaz, o que te falei até agora, serve como base ao que há de vir daqui pra frente. Vamos usar estas ferramentas que
vimos para montar programas estruturados, que o Shell permite. Você verá porque até na TV já teve programa chamado "O
Shell é o Limite".
- Para começar vamos falar dos comandos da família grep.
- grep? Não conheço nenhum termo em inglês com este nome...
- É claro, grep é um acrônimo Global Regular Expression Print, que usa expressões regulares para pesquisar a ocorrência de
cadeias de caracteres na entrada definida (se bem que há uma lenda sobre como este comando foi nomeado: no editor de textos
"ed", o avô do "vim", o comando usado para buscas era g/_expressao regular_/p, ou no inglês g/_re_/p.). Por falar em
expressões regulares (ou regexp), o Aurélio Marinho Jargas tem todas as dicas em sua página (inclusive tutorias) que abordam
o tema. Se você está mesmo a fim de aprender a programar em Shell, Perl, Python, ... Acho bom você ler estes artigos para te
ajudar no que está para vir.
Eu fico com o grep, você com a gripe

Esse negócio de gripe é brincadeira! É só um pretexto para pedir umas caipirinhas. Mas voltando à vaca fria, eu te falei que o
grep procura cadeia de caracteres dentro de uma entrada definida, mas o que vem a ser uma "entrada definida"? Bem, existem
várias formas de definir a entrada do comando grep. Vejamos: Pesquisando em um arquivo:
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ grep rafael
/etc/passwd

Pesquisando em vários arquivos:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ grep grep
*.sh

Pesquisando na saída de comando:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ who | grep
Pelegrino

No 1º exemplo, o mais simples, procurei a palavra rafael em qualquer lugar do arquivo /etc/passwd. Se quisesse procurá-
la como um login name, isto é, somente no início dos registros deste arquivo, eu deveria fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ grep '^rafael'
/etc/passwd

E para que serve este circunflexo e os apóstrofos, você vai me perguntar. O circunflexo (^), se você tivesse lido os artigos
anteriores sobre expressões regulares que te falei, saberia que servem para limitar a pesquisa ao início de cada linha, e
os apóstrofos (') servem para o Shell não interpretar este circunflexo, deixando-o passar incólume para o comando grep.

Olha que legal! O grep aceita como entrada, a saída de outro comando redirecionado por um pipe (isto é muito comum
em Shell e é um tremendo acelerador de execução de comando já que atua como se a saída de um programa fosse
guardada em disco e o segundo programa lesse este arquivo gerado), desta forma, no 3º exemplo, o comando who listou
as pessoas "logadas" na mesma máquina que você (não se esqueça jamais: o Linux é multiusuário) e o grep foi usado
para verificar se o Pelegrino estava trabalhando ou "coçando".

A família grep

Este comando grep é muito conhecido, pois é usado com muita freqüência, o que muitas pessoas desconhecem é que
existem três comandos na família grep, que são:

• grep;
• egrep;
• fgrep.

A principais características diferenciais entre os 3 são:

• O grep pode ou não usar expressões regulares simples, porém no caso de não usá-las, o fgrep é melhor, por ser mais
rápido;
• O egrep ("e" de extended, extendido) é muito poderoso no uso de expressões regulares. Por ser o mais lento da família,
só deve ser usado quando for necessária a elaboração de uma expressão regular não aceita pelo grep;
• O fgrep ("f" de fast, rápido, ou de "file", arquivo) como o nome diz é o rapidinho da família, executa o serviço de forma
muito veloz (por vezes é cerca de 30% mais veloz que o grep e 50% mais que o egrep), porém não permite o uso de
expressões regulares na pesquisa.

Tudo que foi dito acima sobre velocidade, só se aplica à família de comandos grep do Unix. No Linux o grep é
sempre mais veloz, já que os outros dois (fgrep e egrep) são scripts em Shell que chamam o primeiro e, já vou
adiantando, não gosto nem um pouquinho desta solução.
- Agora que você já conhece as diferenças entre os membros da família, me diga: o que você acha dos três exemplos que eu dei
antes das explicações?
- Eu achei que o fgrep resolveria o teu problema de forma mais veloz do que o grep.
- Perfeito! Tô vendo que você está atento! Está entendendo tudo que estou te explicando! Então vamos ver mais exemplos para
clarear de vez as diferenças de uso dos membros da família.

Exemplos

Eu sei que em um arquivo existe um texto falando sobre Linux só não tenho certeza se está escrito com L maiúsculo ou l
minúsculo. Posso fazer de duas formas:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ egrep (Linux |
linux) arquivo.txt

ou

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ grep [Ll]inux
arquivo.txt

No primeiro caso, a expressão regular complexa "(Linux | linux)" usa os parênteses para agrupar as opções e a barra
vertical (|) como um "ou" lógico, isto é, estou procurando Linux ou linux.

No segundo, a expressão regular [Ll]inux significa: começado por L ou l seguido de inux. Por esta expressão ser mais
simples, o grep consegue resolvê-la, portanto acho melhor usar a segunda forma, já que o egrep tornaria a pesquisa mais
lenta.

Outro exemplo. Para listar todos os subdiretórios do diretório corrente, basta:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ ls -l | grep '^d'
drwxr-xr-x 3 root root 4096 Dec 18 2000 doc
drwxr-xr-x 11 root root 4096 Jul 13 18:58 freeciv
drwxr-xr-x 3 root root 4096 Oct 17 2000 gimp
drwxr-xr-x 3 root root 4096 Aug 8 2000 gnome
drwxr-xr-x 2 root root 4096 Aug 8 2000 idl
drwxrwxr-x 14 root root 4096 Jul 13 18:58 locale
drwxrwxr-x 12 root root 4096 Jan 14 2000 lyx
drwxrwxr-x 3 root root 4096 Jan 17 2000 pixmaps
drwxr-xr-x 3 root root 4096 Jul 2 20:30 scribus
drwxrwxr-x 3 root root 4096 Jan 17 2000 sounds
drwxr-xr-x 3 root root 4096 Dec 18 2000 xine

No exemplo que acabamos de ver, o circunflexo (^) serviu para limitar a pesquisa à primeira posição da saída do ls longo.
Os apóstrofos foram colocados para o Shell não "ver" o circunflexo (^).

Vamos ver mais um. Sabemos que as quatro primeiras posições possíveis de um ls -lde um arquivo comum (arquivo
comum! Não é diretório, nem link, nem...) devem ser:

Posição 1ª 2ª 3ª 4ª
- r w x
Valores Possíveis - - s (suid)
-

Assim sendo, para descobrir todos os arquivos executáveis em um determinado diretório eu deveria fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ ls -la | egrep '^-..(x|s)'
-rwxr-xr-x 1 root root 2875 Jun 18 19:38 rc
-rwxr-xr-x 1 root root 857 Aug 9 22:03 rc.local
-rwxr-xr-x 1 root root 18453 Jul 6 17:28 rc.sysinit
Onde novamente usamos o circunflexo (^) para limitar a pesquisa ao início de cada linha, então as linhas listadas serão
as que começam por um traço (-), seguido de qualquer coisa (o ponto quando usado como uma expressão regular
significa qualquer coisa), novamente seguido de qualquer coisa, vindo a seguir um x ou um s.

Obteríamos o mesmo resultado se fizéssemos:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ ls -la | grep
'^-..[xs]'

e agilizaríamos a pesquisa.

Vamos montar uma "cdteca"

Vamos começar a desenvolver programas, acho que a montagem de um banco de dados de músicas é bacana para efeito
didático (e útil nesses tempos de downloads de mp3 e "queimadores" de CDs). Não se esqueça que, da mesma forma que
vamos desenvolver um monte de programas para organizar os seus CDs de música, com pequenas adaptações, você pode
fazer o mesmo com os CDs de software que vêm com a Linux Magazine e outros que você compra ou queima,
disponibilizando este banco de softwareLinux é multiusuário, e como tal deve ser explorado), desta forma ganhando
muitos pontos com seu adorado chefe. para todos que trabalham com você (o Linux é multiusuário, e como tal deve ser
explorado).

- Péra ai! De onde eu vou receber os dados dos CDs?


- Inicialmente, vou lhe mostrar como o seu programa pode receber parâmetros de quem o estiver executando e em breve,
ensinarei a ler os dados pela tela ou de um arquivo.

Passando parâmetros

O layout do arquivo musicas será o seguinte:

nome do álbum^intérprete1~nome da música1:..:intérprete~nome da música

isto é, o nome do álbum será separado por um circunflexo (^) do resto do registro, que é formado por diversos grupos
compostos pelo intérprete de cada música do CD e a respectiva música interpretada. Estes grupos são separados entre si
por dois-pontos (:) e internamente, o intérprete será separado por um til (~) do nome da música.

Eu quero escrever um programa que chamado musinc, que incluirá registros no meu arquivo musicas. Eu passarei o
conteúdo de cada álbum como parâmetro na chamada do programa fazendo assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ musinc
"álbum^interprete~musica:interprete~musica:..."

Desta forma o programa musinc estará recebendo os dados de cada álbum como se fosse uma variável. A única diferença
entre um parâmetro recebido e uma variável é que os primeiros recebem nomes numéricos (nome numérico fica muito
esquisito, né? O que quis dizer é que seus nomes são formados por um e somente um algarismo), isto é $1, $2, $3, ...,
$9. Vamos, antes de tudo, fazer um teste:

Exemplos
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;">$ cat teste
#!/bin/bash
# Programa para testar passagem de parametros
echo "1o. parm -> $1"
echo "2o. parm -> $2"
echo "3o. parm -> $3"

Vamos executá-lo:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;">$ teste passando parametros para testar
bash: teste: cannot execute
Ops! Esqueci-me de torná-lo executável. Vou fazê-lo de forma a permitir que todos possam executá-lo e em seguida vou
testá-lo:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ chmod 755 teste
$ teste passando parametros para testar
1o. parm -> passando
2o. parm -> parametros
3o. parm -> para

Repare que a palavra testar, que seria o quarto parâmetro, não foi listada. Isto deu-se justamente porque o programa
teste só listava os três primeiros parâmetros. Vamos executá-lo de outra forma:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ teste "passando parametros" para testar
1o. parm -> passando parametros
2o. parm -> para
3o. parm -> testar
As aspas não deixaram o Shell ver o espaço em branco entre as palavras e considerou-as um único parâmetro.

Macetes paramétricos

Já que estamos falando em passagem de parâmetros deixa eu te dar mais umas dicas:

Variável Significado
$0 Contém o nome do programa
$# Contém a quantidade de parâmetros passados
$* Contém o conjunto de todos os parâmetros (muito parecido com $@)

Exemplos

Vamos alterar o programa teste para usar as variáveis que acabamos de ver. Vamos fazê-lo assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; font-weight: bold;
color: rgb(0, 0, 0);">$ cat teste
#!/bin/bash
# Programa para testar passagem de parametros (2a. Versao)
echo O programa $0 recebeu $# parametros
echo "1o. parm -> $1"
echo "2o. parm -> $2"
echo "3o. parm -> $3"
echo Todos de uma só \"tacada\": $*

Repare que antes das aspas eu usei uma barra invertida para escondê-las da interpretação do Shell (se não usasse as
contrabarras as aspas não apareceriam). Vamos executá-lo:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ teste passando parametros para testar
O programa teste recebeu 4 parametros
1o. parm -> passando
2o. parm -> parametros
3o. parm -> para
Todos de uma só "tacada": passando parametros para testar

Conforme eu disse, os parâmetros recebem números de 1 a 9, mas isso não significa que não posso usar mais de 9
parâmetros significa somente que só posso endereçar 9. Vamos testar isso:

Exemplo:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat teste
#!/bin/bash
# Programa para testar passagem de parametros (3a. Versao)
echo O programa $0 recebeu $# parametros
echo "11o. parm -> $11"
shift
echo "2o. parm -> $1"
shift 2
echo "4o. Parm -> $1"

Vamos executá-lo:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ teste passando parametros para testar
O programa teste recebeu 4 parametros que são:
11o. parm -> passando1
2o. parm -> parametros
4o. parm -> testar

Duas coisas muito interessantes neste script:

1. Para mostrar que os nomes dos parâmetros variam de $1 a $9 eu fiz um echo $11 e o que aconteceu? O Shell interpretou
como sendo $1 seguido do algarismo 1 e listou passando1;
2. O comando shift cuja sintaxe é shift n, podendo o n assumir qualquer valor numérico (porém seu default é 1 como no
exemplo dado), despreza os n primeiros parâmetros, tornando o parâmetro de ordem n+1, o primeiro ou seja, o $1.

Bem, agora que você já sabe mais sobre passagem de parâmetros do que eu, vamos voltar à nossa "cdteca" para fazer o
script de inclusão de CDs no meu banco chamado musicas. O programa é muito simples (como tudo em Shell) e vou listá-
lo para você ver:

Exemplos
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat musinc
#!/bin/bash
# Cadastra CDs (versao 1)
#
echo $1 >> musicas

O script é fácil e funcional, limito-me a anexar ao fim do arquivo musicas o parâmetro recebido. Vamos cadastrar 3 álbuns
para ver se funciona (para não ficar "enchendo lingüiça", vou supor que em cada CD só existem 2 músicas):

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ musinc
"album 3^Artista5~Musica5:Artista6~Musica5"
$ musinc "album 1^Artista1~Musica1:Artista2~Musica2"
$ musinc "album 2^Artista3~Musica3:Artista4~Musica4"

Listando o conteúdo de musicas.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat musicas
album 3^Artista5~Musica5:Artista6~Musica6
album 1^Artista1~Musica1:Artista2~Musica2
album 2^Artista3~Musica3:Artista4~Musica4

Não está funcional como achava que deveria ficar... podia ter ficado melhor. Os álbuns estão fora de ordem, dificultando a
pesquisa. Vamos alterar nosso script e depois testá-lo novamente:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat musinc
#!/bin/bash
# Cadastra CDs (versao 2)
#
echo $1 >> musicas
sort musicas -o musicas
Vamos cadastrar mais um:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ musinc
"album 4^Artista7~Musica7:Artista8~Musica8"

Agora vamos ver o que aconteceu com o arquivo musicas:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat musicas
album 1^Artista1~Musica1:Artista2~Musica2
album 2^Artista3~Musica3:Artista4~Musica4
album 3^Artista5~Musica5:Artista6~Musica5
album 4^Artista7~Musica7:Artista8~Musica8

Simplesmente inseri uma linha que classifica o arquivo musicas dando a saída nele mesmo (para isso serve a opção -o),
após cada álbum ser anexado.

Oba! Agora está legal e quase funcional. Mas atenção, não se desespere! Esta não é a versão final. O programa ficará
muito melhor e mais amigável, em uma nova versão que desenvolveremos após aprendermos a adquirir os dados da tela
e formatar a entrada.

Exemplos

Ficar listando com o comando cat não está com nada, vamos então fazer um programa chamado muslist para listar um
álbum cujo nome será passado como parâmetro:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat muslist
#!/bin/bash
# Consulta CDs (versao 1)
#
grep $1 musicas

Vamos executá-lo, procurando pelo album 2. Como já vimos antes, para passar a cadeia album 2 é necessário protegê-la
da interpretação do Shell, para que ele não a interprete como dois parâmetros. Vamos fazer assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0, 0);
font-weight: bold;">$ muslist "álbum 2"
grep: can't open 2
musicas: album 1^Artista1~Musica1:Artista2~Musica2
musicas: album 2^Artista3~Musica3:Artista4~Musica4
musicas: album 3^Artista5~Musica5:Artista6~Musica6
musicas: album 4^Artista7~Musica7:Artista8~Musica8

Que lambança! Onde está o erro? Eu tive o cuidado de colocar o parâmetro passado entre aspas, para o Shell não dividi-lo
em dois!

É, mas repare como está o grep executado:

grep $1 musicas

Mesmo colocando álbum 2 entre aspas, para que fosse encarado como um único parâmetro, quando o $1 foi passado pelo
Shell para o comando grep, transformou-se em dois argumentos. Desta forma o conteúdo final da linha, que o comando
grep executou foi o seguinte:

grep album 2 musicas

Como a sintaxe do grep é:

=grep [arq1, arq2, ..., arqn]=

o grep entendeu que deveria procurar a cadeia de caracteres album nos arquivos 2 e musicas, Por não existir o arquivo 2
gerou o erro, e por encontrar a palavra album em todos os registros de musicas, listou a todos.
Sempre que a cadeia de caracteres a ser passada para o comando grep possuir brancos ou TAB,
mesmo que dentro de variáveis, coloque-a sempre entre aspas para evitar que as palavras após o
primeiro espaço em branco ou TAB sejam interpretadas como nomes de arquivos.

Por outro lado, é melhor ignorarmos maiúsculas e minúsculas na pesquisa. Resolveríamos os dois problemas se o
programa tivesse a seguinte forma:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat muslist
#!/bin/bash
# Consulta CDs (versao 2)
#
grep -i "$1" musicas
$ muslist "album 2"
album2^Artista3~Musica3:Artista4~Musica4

Neste caso, usamos a opção -i do grep, que como já vimos, serve para ignorar maiúsculas e minúsculas, e colocamos o
$1 entre aspas, para que o grep continuasse a ver a cadeia de caracteres resultante da expansão da linha pelo Shell como
um único argumento de pesquisa.

Agora repare que o grep localiza a cadeia pesquisada em qualquer lugar do registro, então da forma que estamos
fazendo, podemos pesquisar por álbum, por música, por intérprete ou até por um pedaço de qualquer um destes. Quando
conhecermos os comandos condicionais, montaremos uma nova versão de muslist que permitirá especificar por qual
campo pesquisar.

Aí você vai me dizer:

- Poxa, mas é um saco ter que colocar o argumento de pesquisa entre aspas na hora de passar o nome do álbum. Esta
forma não é nem um pouco amigável!
- Tem razão, e por isso vou te mostrar uma outra forma de fazer o que você pediu:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat muslist
#!/bin/bash
# Consulta CDs (versao 3)
#
grep -i "$*" musicas
$ muslist album 2
album 2^Artista3~Musica3:Artista4~Musica4

Desta forma, o $*, que significa todos os parâmetros, será substituído pela cadeia album 2 (de acordo com o exemplo
anterior, fazendo o que você queria.

Não se esqueça, o problema do Shell não é se você pode ou não fazer uma determinada coisa. O problema é decidir qual
é a melhor forma de fazê-la, já que para desempenhar qualquer tarefa, a quantidade de opções é enorme.

Ah! Em um dia de verão você foi à praia, esqueceu o CD no carro, aquele "solzinho" de 40 graus empenou o seu CD e
agora você precisa de uma ferramenta para removê-lo do banco de dados? Não tem problema, vamos desenvolver um
script chamado musexc, para excluir estes CDs.

Antes de desenvolver o "bacalho", quero te apresentar a uma opção bastante útil da família de comandos grep. É a opção
-v, que quando usada lista todos os registros da entrada, exceto o(s) localizado(s) pelo comando. Vejamos:

Exemplos
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ grep -v "album 2" musicas
album 1^Artista1~Musica1:Artista2~Musica2
album 3^Artista5~Musica5:Artista6~Musica6
album 4^Artista7~Musica7:Artista8~Musica8

Conforme eu expliquei antes, o grep do exemplo listou todos os registros de músicas exceto o referente a album 2,
porque atendia ao argumento do comando. Estamos então prontos para desenvolver o script para remover aquele CD
empenado da sua "CDteca". Ele tem a seguinte cara:
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat musexc
#!/bin/bash
# Exclui CDs (versao 1)
#
grep -v "$1" musicas > /tmp/mus$ $ (dois cifrões juntos)
mv -f /tmp/mus$ $ musicas (dois cifrões juntos)

Na primeira linha mandei para /tpm/mus$ $ (dois cifrões juntos) o arquivo musicas, sem os registros que atendessem a
consulta feita pelo comando grep. Em seguida, movi (que, no duro, equivale a renomear) /tmp/mus$ $ (dois cifrões
juntos) por cima do antigo musicas.

Usei o arquivo /tmp/mus$ $ (dois cifrões juntos) como arquivo de trabalho, porque como já havia citado no artigo
anterior, os dois cifrões juntos contém o PID (Process Identification ou identificação do processo) e desta forma cada um
que editar o arquivo musicas o fará em um arquivo de trabalho diferente, desta forma evitando colisões no uso.

- Aê cara, estes programas que fizemos até aqui estão muito primários em virtude da falta de ferramentas que ainda
temos. Mas é bom, enquanto eu tomo mais um chope, você ir para casa praticar em cima dos exemplos dados porque, eu
prometo, chegaremos a desenvolver um sistema bacana para controle dos seus CDs.
- Quando nos encontrarmos da próxima vez, vou te ensinar como funcionam os comandos condicionais e aprimoraremos
mais um pouco estes scripts.
- Por hoje chega! Já falei demais e preciso molhar a palavra porque estou de goela seca!
- Garçom! Mais um sem colarinho!

Parte III

Trabalhando com cadeias

Pelo título acima não pense você que vou lhe ensinar a ser carcereiro! Estou me referindo à cadeia de caracteres!

O comando cut (que não é a central de trabalhadores)

Primeiro quero te mostrar, de forma eminentemente prática uma instrução simples de usar e muito útil: o comando cut. Esta
instrução é usada para cortar um determinado pedaço de um arquivo e tem duas formas distintas de uso.

O comando cut com a opção -c

Com esta opção, o comando tem a seguinte sintaxe:

cut -c PosIni-PosFim [arquivo]

Onde:

PosIni = Posição inicial


PosFim = Posição final

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat numeros
1234567890
0987654321
1234554321
9876556789
$ cut -c1-5 numeros
12345
09876
12345
98765
$ cut -c-6 numeros
123456
098765
123455
987655
$ cut -c4- numeros
4567890
7654321
4554321
6556789
$ cut -c1,3,5,7,9 numeros
13579
08642
13542
97568
$ cut -c -3,5,8- numeros
1235890
0986321
1235321
9875789

Como dá para ver, no duro mesmo existem quatro sintaxes distintas: na primeira (-c 1-5), eu especifiquei uma faixa, na
segunda (-c -6), especifiquei tudo até uma posição, na terceira (-c 4-) de uma determinada posição em diante e na
quarta (-c 1,3,5,7,9), determinadas posições. A última (-c -3,5, foi só para mostrar que podemos misturar tudo.

O comando cut com a opção -f

Mas não pense você que acabou por aí! Como você deve ter percebido esta forma de cut é útil para arquivos com campos
de tamanho fixo, mas atualmente o que mais existe são arquivos com campos de tamanho variáveis, onde cada campo
termina com um delimitador. Vamos dar uma olhada no arquivo musicas que começamos a preparar no nosso papo na
última vez que viemos aqui no botequim.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat musicas
album 1^Artista1~Musica1:Artista2~Musica2
album 2^Artista3~Musica3:Artista4~Musica4
album 3^Artista5~Musica5:Artista6~Musica5
album 4^Artista7~Musica7:Artista8~Musica8

Então, recapitulando, o seu "leiaute" é o seguinte:

nome do album^interprete1~nome da musica1:...:interpreten~nome da musican

isto é, o nome do álbum será separado por um circunflexo (^) do resto do registro, que é formado por diversos grupos
compostos pelo intérprete de cada música do CD e a respectiva música interpretada. Estes grupos são separados entre si
por dois-pontos (:) e internamente, o nome do intérprete será separado por um til (~) do nome da música.

Então para pegarmos os dados referentes a todas as segundas músicas do arquivo musicas, devemos fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cut -f2 -d: musicas
Artista2~Musica2
Artista4~Musica4
Artista6~Musica5
Artista8~Musica8

Ou seja, cortamos o segundo campo (-f de field em inglês) delimitado (-d) por dois-pontos (:). Mas, se quisermos
somente os intérpretes, devemos fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cut -f2 -d: musicas | cut -f1 -d~
Artista2
Artista4
Artista6
Artista8

Para entender isso, vamos pegar a primeira linha de musicas:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ head -1 musicas
album 1^Artista1~Musica1:Artista2~Musica2
Então observe o que foi feito:

Delimitador do primeiro cut (:)

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">album
1^Artista1~Musica1:Artista2~Musica2

Desta forma, no primeiro cut, o primeiro campo do delimitador (-d) dois-pontos (:) é album 1^Artista1~Musica1 e o segundo,
que é o que nos interessa, é Artista2~Musica2.

Vamos então ver o que aconteceu no segundo cut:

Novo delimitador (~)

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">Artista2~Musica2

Agora, primeiro campo do delimitador (-d) til (~), que é o que nos interessa, é Artista2 e o segundo é Musica2.

Se o raciocínio que fizemos para a primeira linha for aplicado no restante do arquivo, chegaremos à resposta anteriormente
dada.

O comando tr

Outro comando muito interessante é o tr que serve para substituir, comprimir ou remover caracteres. Sua sintaxe segue o
seguinte padrão:

tr [opções] cadeia1 [cadeia2]

O comando tr copia o texto da entrada padrão (stdin), troca as ocorrência dos caracteres de cadeia1 pelo seu
correspondente na cadeia2 ou troca múltiplas ocorrências dos caracteres de cadeia1 por somente um caractere, ou ainda
caracteres da cadeia1.

As principais opções do comando são:

Opção Significado
-s Comprime n ocorrências de cadeia1 em apenas uma
-d Remove os caracteres de cadeia1

Trocando caracteres com tr

Primeiro vou te dar um exemplo bem bobo:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ echo bobo | tr o a
baba

Isto é, troquei todas as ocorrências da letra o pela letra a.

Suponha que em um determinado ponto do meu script eu peça ao operador para teclar sn (sim ou não), e guardo sua
resposta na variável $Resp. Ora o conteúdo de $Resp pode estar com letra maiúscula ou minúscula, e desta forma eu
teria que fazer diversos testes para saber se a resposta dada foi S, s, N ou n. Então o melhor é fazer: ou
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ Resp=$(echo
$Resp | tr SN sn)

e após este comando eu teria certeza que o conteúdo de $Resp seria um s ou um n.

Se o meu arquivo ArqEnt está todo escrito com letras maiúsculas e desejo passá-las para minúsculas eu faço:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ tr A-Z a-z <
ArqEnt > /tmp/ArqSai
$ mv -f /tmp/ArqSai ArqEnt
Note que neste caso usei a notação A-Z para não escrever ABCD...YZ. Outro tipo de notação que pode ser usada são as escape
sequences (prefiro escrever no bom e velho português, mas nesse caso como eu traduziria? Seqüências de escape? Meio sem
sentido, né? Mas vá lá...) que também são reconhecidas por outros comandos e também na linguagem C, e cujo significado você
verá a seguir:
Seqüência Significado Octal
\t Tabulação \011
\n Nova linha \012
\v Tabulação Vertical \013
\f Nova Página \014
\r Início da linha <^M> \015
\ Uma barra invertida \0134

Removendo caracteres com tr

Então deixa eu te contar um "causo": um aluno que estava danado comigo, resolveu complicar a minha vida e em um
exercício prático valendo nota que passei para ser feito no computador, me entregou o script com todos os comandos
separados por ponto-e-vírgula (lembra que eu disse que o ponto-e-vírgula servia para separar diversos comandos em uma
mesma linha?).

Vou dar um exemplo simplificado e idiota de uma "tripa" assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ cat confuso
echo leia Programação Shell Linux do Julio Cezar Neves > livro;cat livro;pwd;ls;rm -f livro 2>/dev/null;cd ~

Eu executava o programa e ele funcionava:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ confuso
leia Programação Shell Linux do Julio Cezar Neves
/home/jneves/LM
confuso livro musexc musicas musinc muslist numeros

Mas nota de prova é coisa séria (e nota de dólar é mais ainda ) então, para entender o que o aluno havia feito, o
chamei e em sua frente executei o seguinte comando:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ tr ";" "\n" <
confuso
echo leia Programação Shell Linux do Julio Cezar Neves
pwd
cd ~
ls -l
rm -f lixo 2>/dev/null

O cara ficou muito desapontado, porque em 2 ou 3 segundos eu desfiz a gozação que ele perdera horas para fazer.

Mas preste atenção! Se eu estivesse em uma máquina com Unix, eu teria feito:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">$ tr ";" "\012" < confuso
Xpremendo com tr

Agora veja a diferença entre os dois comandos date: o que fiz hoje e outro que foi executado há duas semanas:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ date # Hoje
Sun Sep 19 14:59:54 2004
$ date # Há duas semanas
Sun Sep 5 10:12:33 2004

Para pegar a hora eu deveria fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ date | cut -f
4 -d ' '
14:59:54

Mas duas semanas antes ocorreria o seguinte:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-


background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-
initial; color: rgb(0, 0, 0);">$ date | cut -f 4 -d ' '
5

Mas observe porque:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ date # Há
duas semanas
Sun Sep 5 10:12:33 2004

Como você pode notar, existem 2 caracteres em branco antes do 5 (dia), o que estraga tudo porque o terceiro pedaço
está vazio e o quarto é o dia (5). Então o ideal seria comprimir os espaços em brancos sucessivos em somente um espaço
para poder tratar as duas cadeias resultantes do comando date da mesma forma, e isso se faz assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ date | tr -s "
"
Sun Sep 5 10:12:33 2004

E agora eu poderia cortar:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ date | tr -s "
" | cut -f 4 -d " "
10:12:33

Olha só como o Shell já está quebrando o galho. Veja este arquivo que foi baixado de uma máquina com aquele sistema
operacional que pega vírus:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ cat -ve
ArqDoDOS.txt
Este arquivo^M$
foi gerado pelo^M$
DOS/Rwin e foi^M$
baixado por um^M$
ftp mal feito.^M$
E agora eu quero te dar duas dicas:
Dica #1 - A opção -v do cat mostra os caracteres de controle invisíveis, com a notação ^L, onde ^ é a tecla control e L é a
respectiva letra. A opção -e$). mostra o final da linha como um cifrão.
Dica #2 - Isto ocorre porque no formato DOS (ou rwin), o fim dos registros é formado por um Carriage-Return (\r) e um line-
feed (\f). No Linux porém o final do registro tem somente o line-feed.

Vamos então limpar este arquivo.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">$ tr -d '\r' < ArqDoDOS.txt >
/tmp/ArqDoLinux.txt
$ mv -f /tmp/ArqDoLinux.txt ArqDoDOS.txt

Agora vamos ver o que aconteceu:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-


background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-
initial; color: rgb(0, 0, 0);">$ cat -ve ArqDoDOS.txt
Este arquivo$
foi gerado pelo$
DOS/Rwin e foi$
baixado por um$
ftp mal feito.$

Bem a opção -d do tr remove o caractere especificado de todo o arquivo. Desta forma eu removi os caracteres
indesejados salvando em um arquivo de trabalho e posteriormente renomeei-o para a sua designação original.

Obs: No Unix eu deveria fazer:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">$ tr -d '\015' < ArqDoDOS.txt >
/tmp/ArqDoLinux.txt

Isto aconteceu porque o ftp foi feito do modo binário (ou image), isto é, sem a interpretação do texto. Se antes da
transmissão do arquivo tivesse sido estipulada a opção ascii do ftp, isto não teria ocorrido.

- Olha, depois desta dica tô começando a gostar deste tal de Shell, mas ainda tem muita coisa que não consigo fazer.

- Pois é, ainda não te falei quase nada sobre programação em Shell, ainda tem muita coisa para aprender, mas com o que
aprendeu, já dá para resolver muitos problemas, desde que você adquira o “modo Shell de pensar”. Você seria capaz de
fazer um script para me dizer quais são as pessoas que estão “logadas” há mais de um dia no seu servidor?

- Claro que não! Para isso seria necessário eu conhecer os comandos condicionais que você ainda não me explicou como
funcionam.

- Deixa eu tentar mudar um pouco a sua lógica e trazê-la para o “modo Shell de pensar”, mas antes é melhor tomarmos
um chope... Ô Chico, traz mais dois...

- Agora que já molhei a palavra, vamos resolver o problema que te propus. Repare como funciona o comando who:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ who
jneves pts/1 Sep 18 13:40
rtorres pts/0 Sep 20 07:01
rlegaria pts/1 Sep 20 08:19
lcarlos pts/3 Sep 20 10:01

E veja também o date:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ date
Mon Sep 20 10:47:19 BRT 2004

Repare que o mês e o dia estão no mesmo formato em ambos os comandos.

Algumas vezes um comando tem a saída em português e o outro em inglês. Quando isso ocorrer, você pode usar o seguinte
artifício:
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ date
Mon Sep 20 10:47:19 BRT 2004
$ LANG=pt_BR date
Seg Set 20 10:47:19 BRT 2004
Desta forma passando a saída do comando date para português.

Ora, se em algum registro do who eu não encontrar a data de hoje, é sinal que o cara está "logado" há mais de um dia, já
que ele não pode ter se "logado" amanhã... Então vamos guardar o pedaço que importa da data de hoje para procurá-la
na saída do who:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;">$ Data=$(date | cut -f 2-3 -d' ')

Eu usei a construção $(...), para priorizar a execução dos comandos antes de atribuir a sua saída à variável $Data. Vamos
ver se funcionou:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ echo $Data
Sep 20

Beleza! Agora, o que temos que fazer é procurar no comando who os registros que não possuem esta data.

- Ah! Eu acho que estou entendendo! Você falou em procurar e me ocorreu o comando grep, estou certo?

- Certíssimo! Só que eu tenho que usar o grep com aquela opção que ele só lista os registros nos quais ele não encontrou
a cadeia. Você se lembra que opção é essa?

- Claro, é a opção -v...

- Isso! Tá ficando bão! Então vamos ver:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ who | grep -v
"$Data"
jneves pts/1 Sep 18 13:40

- E se eu quisesse mais um pouco de perfumaria eu faria assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: rgb(0, 0, 0);">$ who | grep -v
"$Data" | cut -f1 -d ' '
jneves

- Viu? Não foi necessário usar nenhum comando condicional, até porque o nosso mais usado comando condicional, o
famoso if, não testa condição, mas sim instruções, como veremos agora.

Comandos condicionais

Veja as linhas de comando a seguir:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ ls musicas
musicas
$ echo $?
0
$ ls ArqInexistente
ls: ArqInexistente: No such file or directory
$ echo $?
1
$ who | grep jneves
jneves pts/1 Sep 18 13:40 (10.2.4.144)
$ echo $?
0
$ who | grep juliana
$ echo $?
1

- O que é esse $? faz aí? Começando por cifrão ($) parece ser uma variável, certo?

- Sim é uma variável que contém o código de retorno da última instrução executada. Posso te garantir que se esta
instrução foi bem sucedida, $? terá o valor zero, caso contrário seu valor será diferente de zero.
O Comando if

O que o nosso comando condicional if faz é testar a variável $?. Então vamos ver a sua sintaxe:

if cmd

then

cmd1

cmd2

cmdn

else

cmd3

cmd4

cmdm

fi

ou seja: caso comando cmdtenha sido executado com sucesso, os comandos do bloco do then (cmd1, cmd2 e cmdn)
serão executados, caso contrário, os comandos executados serão os do bloco opcional do else (cmd3, cmd4 e cmdm),
terminando com um fi.

Vamos ver na prática como isso funciona usando um scriptizinho que serve para incluir usuários no /etc/passwd:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat incusu
#!/bin/bash
# Versão 1
if grep ^$1 /etc/passwd
then
echo Usuario '$1' já existe
else
if useradd $1
then
echo Usuário '$1' incluído em /etc/passwd
else
echo "Problemas no cadastramento. Você é root?"
fi
fi

Repare que o if está testando direto o comando grepe esta é a sua finalidade. Caso o if$1) seja bem sucedido, ou seja, o
usuário (cujo nome está em foi encontrado em /etc/passwd, os comandos do bloco do thenserão executados (neste
exemplo é somente o echo) e caso contrário, as instruções do bloco do elseé que serão executadas, quando um novo
iftesta se o comando useraddfoi executado a contento, criando o registro do usuário em /etc/passwd, ou não quando dará
a mensagem de erro.

Vejamos sua execução, primeiramente passando um usuário já cadastrado:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ incusu jneves
jneves:x:54002:1001:Julio Neves:/home/jneves:/bin/bash
Usuario 'jneves' ja existe

Como já vimos diversas vezes, mas é sempre bom insistir no tema para que você já fique precavido, no exemplo dado
surgiu uma linha indesejada, ela é a saída do comando grep. Para evitar que isso aconteça, devemos desviar a saída
desta instrução para /dev/null, ficando assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat incusu
#!/bin/bash
# Versão 2
if grep ^$1 /etc/passwd > /dev/null
then
echo Usuario '$1' já existe
else
if useradd $1
then
echo Usuário '$1' incluído em /etc/passwd
else
echo "Problemas no cadastramento. Você é root?"
fi
fi

Agora vamos testá-lo como usuário normal (não root):

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ incusu ZeNinguem
./incusu[6]: useradd: not found
Problemas no cadastramento. Você é root?

Epa, aquele erro não era para acontecer! Para evitar que isso aconteça devemos mandar também a saída de erro (strerr,
lembra?) do useradd para /dev/null, ficando na versão final assim:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat incusu
#!/bin/bash
# Versão 3
if grep ^$1 /etc/passwd > /dev/null
then
echo Usuario '$1' já existe
else
if useradd $1 2> /dev/null
then
echo Usuário '$1' incluído em /etc/passwd
else
echo "Problemas no cadastramento. Você é root?"
fi
fi

Depois destas alterações e de fazer um su – (me tornar root) vejamos o seu comportamento:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ incusu botelho
Usuário 'botelho' incluido em /etc/passwd

E novamente:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ incusu botelho
Usuário 'botelho' já existe

Lembra que eu falei que ao longo dos nossos papos e chopes os nossos programas iriam se aprimorando? Então vejamos
agora como poderíamos melhorar o nosso programa para incluir músicas:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat musinc
#!/bin/bash
# Cadastra CDs (versao 3)
#
if grep "^$1$" musicas > /dev/null
then
echo Este álbum já está cadastrado
else
echo $1 >> musicas
sort musicas -o musicas
fi
Como você viu, é uma pequena evolução da versão anterior, assim, antes de incluir um registro (que pela versão anterior
poderia ser duplicado), testamos se o registro começava (^) e terminava ($) igual ao parâmetro passado ($1). O uso do
circunflexo (^) no início da cadeia e cifrão ($) no fim, são para testar se o parâmetro passado (o álbum e seus dados) são
exatamente iguais a algum registro anteriormente cadastrado e não somente igual a um pedaço de algum dos registros.

Vamos executá-lo passando um álbum já cadastrado:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ musinc "album 4^Artista7~Musica7:Artista8~Musica8"
Este álbum já está cadastrado

E agora um não cadastrado:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ musinc "album 5^Artista9~Musica9:Artista10~Musica10"
$ cat musicas
album 1^Artista1~Musica1:Artista2~Musica2
album 2^Artista3~Musica3:Artista4~Musica4
album 3^Artista5~Musica5:Artista6~Musica5
album 4^Artista7~Musica7:Artista8~Musica8
album 5^Artista9~Musica9:Artista10~Musica10

- Como você viu, o programa melhorou um pouquinho, mas ainda não está pronto. À medida que eu for te ensinando a
programar em shell, nossa CDteca irá ficando cada vez melhor.

- Entendi tudo que você me explicou, mas ainda não sei como fazer um ifpara testar condições, ou seja, o uso normal do
comando.

- Cara, para isso existe o comando test, ele é que testa condições. O comando iftesta o comando test. Mas isso está meio
confuso e como já falei muito, estou precisando de uns chopes para molhar a palavra. Vamos parando por aqui e na
próxima vez te explico direitinho o uso do teste de diversas outras sintaxes do if.

- Falou! Acho bom mesmo porque eu também já tô ficando zonzo e assim tenho tempo para praticar esse monte de coisas
que você me falou hoje.

- Para fixar o que você aprendeu, tente fazer um scriptizinho para informar se um determinado usuário, que será passado
como parâmetro esta logado (arghh!) ou não.

- Aê Chico, mais dois chopes por favor...

Parte IV

Diálogo

- E aí cara, tentou fazer o exercício que te pedi para revigorar as idéias?

- Claro, que sim! Em programação, se você não treinar, não aprende. Você me pediu para fazer um scriptizinho para
informar se um determinado usuário, que será passado como parâmetro está logado (arghh!) ou não. Eu fiz o seguinte:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat logado
#!/bin/bash
# Pesquisa se uma pessoa está logada ou não
if who | grep $1
then
echo $1 está logado
else
echo $1 não se encontra no pedaço
fi

- Calma rapaz! Já vi que você chegou cheio de tesão, primeiro vamos pedir os nossos chopes de praxe e depois vamos ao
Shell. Chico traz dois chopes, um sem colarinho!

- Agora que já molhamos os nossos bicos, vamos dar uma olhadinha na execução do seu bacalho:
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ logado jneves
jneves pts/0 Oct 18 12:02 (10.2.4.144)
jneves está logado

Realmente funcionou. Passei o meu login como parâmetro e ele disse que eu estava logado, porém ele mandou uma linha
que eu não pedi. Esta linha é a saída do comando who, e para evitar que isso aconteça é só mandá-la para o buraco
negro que a esta altura você já sabe que é o /dev/null. Vejamos então como ficaria:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat logado
#!/bin/bash
# Pesquisa se uma pessoa está logada ou não (versão 2)
if who | grep $1 > /dev/null
then
echo $1 está logado
else
echo $1 não se encontra no pedaço
fi

Agora vamos aos testes:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ logado jneves
jneves está logado
$ logado chico
chico não se encontra no pedaço

Ah, agora sim! Lembre-se desta pegadinha, a maior parte dos comandos tem uma saída padrão e uma saída de
erros (o grep é uma das poucas exceções, já que não dá mensagem de erro quando não acha uma cadeia) e é necessário
estarmos atentos para redirecioná-las para o buraco negro quando necessário.
Bem, agora vamos mudar de assunto: na última vez que nos encontramos aqui no Botequim, eu estava te mostrando os
comandos condicionais e, quando já estávamos de goela seca falando sobre o if, você me perguntou como se testa condições.
Vejamos então o comando test

O comando Test

Bem, todos estamos acostumados a usar o if testando condições, e estas são sempre, maior, menor, maior ou igual,
menor ou igual, igual e diferente. Bem, em Shell para testar condições, usamos o comando test, só que ele é muito mais
poderoso que o que estamos habituados. Primeiramente vou te mostrar as principais opções (existem muitas outras) para
testarmos arquivos em disco:

Opções do Comando test para arquivos


Opção Verdadeiro se:
-e arq arq existe
-s arq arq existe e tem tamanho maior que zero
-f arq arq existe e é um arquivo regular
-d arq arq existe e é um diretório;
-r arq arq existe e com direito de leitura
-w arq arq existe e com direito de escrita
-x arq arq existe e com direito de execução

Veja agora as principais opções para teste de cadeias de caracteres:

Opções do comando test para cadeias de caracteres


Opção Verdadeiro se:
-z cadeia Tamanho de cadeia é zero
-n cadeia Tamanho de cadeia é maior que zero
cadeia A cadeia cadeia tem tamanho maior que zero
c1 = c2 Cadeia c1 e c2 são idênticas

E pensa que acabou? Engano seu! Agora é que vem o que você está mais acostumado, ou seja as famosas comparações
com numéricos. Veja a tabela:

Opções do comando test para números


Opção Verdadeiro se: Significado
n1 -eq n2 n1 e n2 são iguais equal
n1 -ne n2 n1 e n2 não são iguais not equal
n1 -gt n2 n1 é maior que n2 greater than
n1 -ge n2 n1 é maior ou igual a n2 greater or equal
n1 -lt n2 n1 é menor que n2 less than
n1 -le n2 n1 é menor ou igual a n2 less or equal

Além de tudo, some-se a estas opções as seguintes facilidades:

Operadores
Operador Finalidade
Parênteses Agrupar
Exclamação ! Negar
-a E lógico
-o OU lógico

Ufa! Como você viu tem coisa prá chuchu, e como eu te disse no início, o nosso if é muito mais poderoso que o dos
outros. Vamos ver em uns exemplos como isso tudo funciona, primeiramente testaremos a existência de um diretório:

Exemplos:

if test -d lmb

then

cd lmb

else

mkdir lmb

cd lmb

fi

No exemplo, testei se existia um diretório lmb definido, caso negativo (else), ele seria criado. Já sei, você vai criticar a
minha lógica dizendo que o script não está otimizado. Eu sei, mas queria que você o entendesse assim, para então poder
usar o ponto-de-espantação (!) como um negador do test. Veja só:

if test ! -d lmb

then

mkdir lmb

fi

cd lmb

Desta forma o diretório lmb seria criado somente se ele ainda não existisse, e esta negativa deve-se ao ponto-de-
exclamação (!) precedendo a opção -d. Ao fim da execução deste fragmento de script, o programa estaria com certeza
dentro do diretório lmb.

Vamos ver dois exemplos para entender a diferença comparação entre números e entre cadeias.
cad1=1

cad2=01

if test $cad1 = $cad2

then

echo As variáveis são iguais.

else

echo As variáveis são diferentes.

fi

Executando o fragmento de programa acima vem:

As variáveis são diferentes.

Vamos agora alterá-lo um pouco para que a comparação seja numérica:

cad1=1

cad2=01

if test $cad1 -eq $cad2

then

echo As variáveis são iguais.

else

echo As variáveis são diferentes.

fi

E vamos executá-lo novamente:

As variáveis são iguais.

Continuação do comando test

Como você viu nas duas execuções obtive resultados diferentes porque a cadeia 011, porém, a coisa muda quando as
variáveis são testadas numericamente, já que o número 1 é igual ao número 01. é realmente diferente da cadeia

Exemplos:

Para mostrar o uso dos conectores -o (OU) e -a (E), veja um exemplo animal feito direto no prompt (me desculpem os
zoólogos, mas eu não entendendo nada de reino, filo, classe, ordem, família, gênero e espécie, desta forma o que estou
chamando de família ou de gênero tem grande chance de estar incorreto):

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ Familia=felinae
$ Genero=gato
$ if test $Familia = canidea -a $Genero = lobo -o $Familia = felina -a $Genero = leão
> then
> echo Cuidado
> else
> echo Pode passar a mão
> fi
Pode passar a mão

Neste exemplo caso o animal fosse da família canídea E (-a) do gênero lobo, OU (-o) da familia felina E (-a) do gênero
leão, seria dado um alerta, caso contrário a mensagem seria de incentivo.
Os sinais de maior (>) no início das linhas internas ao if são os prompts de continuação (que estão
definidos na variável $PS2) e quando o Shellidentifica que um comando continuará na linha seguinte,
automaticamente ele o coloca até que o comando seja encerrado.

Vamos mudar o exemplo para ver se continua funcionando:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ Familia=felino
$ Genero=gato
$ if test $Familia = felino -o $Familia = canideo -a $Genero = onça -o $Genero = lobo
> then
> echo Cuidado
> else
> echo Pode passar a mão
> fi
Cuidado

Obviamente a operação redundou em erro, isto foi porque a opção -a tem precedência sobre a -o, e desta forma o que
primeiro foi avaliado foi a expressão:

$Familia = canideo -a $Genero = onça

Que foi avaliada como falsa, retornando o seguinte:

$Familia = felino -o FALSO -o $Genero = lobo

Que resolvida vem:

VERDADEIRO -o FALSO -o FALSO

Como agora todos conectores são -o, e para que uma série de expressões conectadas entre si por diversos OU lógicos
seja verdadeira, basta que uma delas seja, a expressão final resultou como VERDADEIRO e o then foi executado de forma
errada. Para que isso volte a funcionar façamos o seguinte:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ if test \($Familia = felino -o $Familia = canideo\) -a \($Genero = onça -o $Genero = lobo\)
> then
> echo Cuidado
> else
> echo Pode passar a mão
> fi
Pode passar a mão

Desta forma, com o uso dos parênteses agrupamos as expressões com o conector -o, priorizando as suas execuções e
resultando:

VERDADEIRO -a FALSO

Para que seja VERDADEIRO o resultado duas expressões ligadas pelo conector -a é necessário que ambas sejam
verdadeiras, o que não é o caso do exemplo acima. Assim o resultado final foi FALSO sendo então o else corretamente
executado.

Se quisermos escolher um CD que tenha faixas de 2 artistas diferentes, nos sentimos tentados a usar um if com o
conector -a, mas é sempre bom lembrarmos que o bash nos dá muito recursos, e isso poderia ser feito de forma muito
mais simples com um único comando grep, da seguinte maneira:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ grep Artista1
musicas | grep Artista2

Da mesma forma para escolhermos CDs que tenham a participação do Artista1 e do Artista2, não é necessário montarmos
um if com o conector -o. O egrep (ou grep -E, sendo este mais aconselhável) também resolve isso para nós. Veja como:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ egrep
(Artista1|Artista2) musicas
Ou (nesse caso específico) o próprio grep puro e simples poderia nos quebrar o galho:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ grep
Artista[12] musicas

No egrep acima, foi usada uma expressão regular, onde a barra vertical (|) trabalha como um OU lógico e os parênteses
são usados para limitar a amplitude deste OU. Já no grep da linha seguinte, a palavra Artista deve ser seguida por um dos
valores da lista formada pelos colchetes ([ ]), isto é, 1 ou 2.

- Tá legal, eu aceito o argumento, o if do Shell é muito mais poderoso que os outros caretas, mas cá pra nós, essa
construção de if test ... é muito esquisita, é pouco legível.

- É você tem razão, eu também não gosto disso e acho que ninguém gosta. Acho que foi por isso, que o Shell incorporou
outra sintaxe que substitui o comando test.

Exemplos

Para isso vamos pegar aquele exemplo para fazer uma troca de diretórios, que era assim:

if test ! -d lmb

then

mkdir lmb

fi

cd lmb

e utilizando a nova sintaxe, vamos fazê-lo assim:

if [ ! -d lmb ]

then

mkdir lmb

fi

cd lmb

Ou seja, o comando test pode ser substituído por um par de colchetes ([ ]), separados por espaços em branco dos
argumentos, o que aumentará enormemente a legibilidade, pois o comando if irá ficar com a sintaxe semelhante à das
outras linguagens e por isso este será o modo que o comando test será usado daqui para a frente.

Querida, encolheram o comando condicional

Se você pensa que acabou, está muito enganado. Repare a tabela (tabela verdade) a seguir:

Valores Booleanos E OU
VERDADEIRO-VERDADEIRO VERDADEIRO VERDADEIRO
VERDADEIRO-FALSO FALSO VERDADEIRO
FALSO-VERDADEIRO FALSO VERDADEIRO
FALSO-FALSO FALSO FALSO

Ou seja, quando o conector é E e a primeira condição é verdadeira, o resultado final pode ser VERDADEIRO ou FALSO,
dependendo da segunda condição, já no conector OU, caso a primeira condição seja verdadeira, o resultado sempre será
VERDADEIRO e se a primeira for falsa, o resultado dependerá da segunda condição.

Ora, os caras que desenvolveram o interpretador não são bobos e estão sempre tentando otimizar ao máximo os
algoritmos. Portanto, no caso do conector E, a segunda condição não será avaliada, caso a primeira seja falsa, já que o
resultado será sempre FALSO. Já com o OU, a segunda será executada somente caso a primeira seja falsa.
Aproveitando disso, criaram uma forma abreviada de fazer testes. Batizaram o conector E de && e o OU de || e para ver
como isso funciona, vamos usá-los como teste no nosso velho exemplo de trocarmos de diretório, que em sua última
versão estava assim:

if [ ! -d lmb ]

then

mkdir lmb

fi

cd lmb

Isso também poderia ser escrito da seguinte maneira:

[ ! -d lmb ] && mkdir lmb

cd dir

Ou ainda retirando a negação (!):

[ -d lmb ] || mkdir lmb

cd dir

No primeiro caso, se o primeiro comando (o test que está representado pelos colchetes) for bem sucedido, isto é, não
existir o diretório lmb, o mkdir será efetuado porque a primeira condição era verdadeira e o conector era E.

No exemplo seguinte, testamos se o diretório lmb existia (no anterior testamos se ele não existia) e caso isso fosse
verdade, o mkdir não seria executado porque o conector era OU. Outra forma:

cd lmb || mkdir lmb

Neste caso, se o cd fosse mal sucedido, seria criado o diretório lmb mas não seria feito o cd para dentro dele. Para
executarmos mais de um comando desta forma, é necessário fazermos um grupamento de comandos, e isso se consegue
com o uso de chaves ({ }). Veja como seria o correto:

cd lmb ||

mkdir lmb

cd lmb

Ainda não está legal, porque caso o diretório não exista, o cd dará a mensagem de erro correspondente. Então devemos
fazer:

cd lmb 2> /dev/null ||

mkdir lmb

cd lmb

Como você viu o comando if nos permitiu fazer um cd seguro de diversas maneiras. É sempre bom lembrarmos que o
seguro a que me referi é no tocante ao fato de que ao final da execução você sempre estará dentro de lmb, desde que
você tenha permissão para entrar em lmb, permissão para criar um diretório em ../lmb, haja espaço em disco, ...

E tome de test
Ufa! Você pensa que acabou? Ledo engano! Ainda tem uma forma de test a mais. Essa é legal porque ela te permite usar
padrões para comparação. Estes padrões atendem às normas de Geração de Nome de Arquivos ( File Name Generation,
que são ligeiramente parecidas com as Expressões Regulares, mas não podem ser confundidas com estas). A diferença de
sintaxe deste para o test que acabamos de ver é que esse trabalha com dois pares de colchete da seguinte forma:

[[ expressão ]]

Onde expressão é uma das que constam na tabela a seguir:

Expressões Condicionais Para Padrões


Expressão Retorna
cadeia == padrão
Verdadeiro se cadeia1 casa com padrão
cadeia1 = padrao
cadeia1 ! padrao Verdadeiro se cadeia1 não casa com padrao.
cadeia1 < cadeia1 Verdadeiro se cadeia1 vem antes de cadeia1 alfabeticamente.
cadeia1 > cadeia1 Verdadeiro se cadeia1 vem depois de cadeia1 alfabeticamente
expr1 && expr2 "E" lógico, verdadeiro se ambos expr1 e expr2 são verdadeiros
expr1 ¦¦ expr2 "OU" lógico, verdadeiro se expr1 ou expr2 for verdadeiro
http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-
initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ echo $H
13
$ [[ $H == [0-9] || $H == 1[0-2] ]] || echo Hora inválida
Hora inválida
$H=12
$ [[ $H == [0-9] || $H == 1[0-2] ]] || echo Hora inválida
$

Neste exemplo, testamos se o conteúdo da variável $H estava compreendido entre zero e nove ([0-9]) ou (||) se estava
entre dez e doze (1[0-2]), dando uma mensagem de erro caso não fosse.

Exemplos:

Para saber se uma variável tem o tamanho de um e somente um caractere, faça:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify;">$ var=a
$ [[ $var == ? ]] && echo var tem um caractere
var tem um caractere
$ var=aa
$ [[ $var == ? ]] && echo var tem um caractere
$

Como você pode imaginar, este uso de padrões para comparação, aumenta muito o poderio do comando test. No início
deste papo, antes do último chope, afirmamos que o comando if do interpretador Shell é mais poderoso que o seu similar
em outras linguagens. Agora que conhecemos todo o seu espectro de funções, diga-me: você concorda ou não com esta
assertiva?

Acaso casa com case

Vejamos um exemplo didático: dependendo do valor da variável $opc o script deverá executar uma uma das opções:
inclusão, exclusão, alteração ou fim. Veja como ficaria este fragmento de script:

if [ $opc -eq 1 ]

then

inclusao

elif [ $opc -eq 2 ]

then

exclusao
elif [ $opc -eq 3 ]

then

alteracao

elif [ $opc -eq 4 ]

then

exit

else

echo Digite uma opção entre 1 e 4

fi

Neste exemplo você viu o uso do elif com um else if, esta á a sintaxe válida e aceita, mas poderíamos fazer melhor, e isto
seria com o comando case, que tem a sintaxe a seguir:

case $var in

padrao1) cmd1

cmd2

cmdn ;;

padrao2) cmd1

cmd2

cmdn ;;

padraon) cmd1

cmd2

cmdn ;;

esac

Onde a variável $var é comparada aos padrões padrao1, ..., padraon e caso um deles atenda, o bloco de comandos cmd1,
..., cmdn correspondente é executado até encontrar um duplo ponto-e-vírgula (;;), quando o fluxo do programa se
desviará para instrução imediatamente após o esac.

Na formação dos padrões, são aceitos os seguintes caracteres:

Caracteres Para Formação de Padrões


Caractere Significado
* Qualquer caractere ocorrendo zero ou mais vezes
? Qualquer caractere ocorrendo uma vez
[...] Lista de caracteres
¦ OU lógico

Para mostrar como fica melhor, vamos repetir o exemplo anterior, só que desta vez usaremos o case e não o if ... elif ...
else ... fi.

case $opc in

1) inclusao ;;

2) exclusao ;;

3) alteracao ;;
4) exit ;;

*) echo Digite uma opção entre 1 e 4

esac

Como você deve ter percebido, eu usei o asterisco como a última opção, isto é, se o asterisco atende a qualquer coisa,
então ele servirá para qualquer coisa que não esteja no intervalo de 1 a 4. Outra coisa a ser notada é que o duplo ponto-
e-vírgula não é necessário antes do esac.

Exemplos:

Vamos agora fazer um script mais radical. Ele te dará bom dia, boa tarde ou boa noite dependendo da hora que for
executado, mas primeiramente veja estes comandos:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(255,
255, 0);">$ date
Tue Nov 9 19:37:30 BRST 2004
$ date +%H
19

O comando date informa a data completa do sistema, mas ele tem diversas opções para seu mascaramento. Neste
comando, a formatação começa com um sinal de mais (+) e os caracteres de formatação vêm após um sinal de
percentagem (%), assim o %H significa a hora do sistema. Dito isso vamos ao exemplo:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">$ cat boasvindas.sh
#!/bin/bash
# Programa bem educado que
# dá bom-dia, boa-tarde ou
# boa-noite conforme a hora
Hora=$(date +%H)
case $Hora in
0? | 1[01]) echo Bom Dia
;;
1[2-7] ) echo Boa Tarde
;;
* ) echo Boa Noite
;;
esac
exit

Peguei pesado, né? Que nada vamos esmiuçar a resolução caso-a-caso (ou seria case-a-case? )

0? | 1[01] - Significa zero seguido de qualquer coisa (?), ou (|) um seguido de zero ou um ([01]) ou seja, esta linha
pegou 01, 02, ... 09, 10 e 11;

1[2-7] - Significa um seguido da lista de dois a sete, ou seja, esta linha pegou 12, 13, ... 17;

*- Significa tudo que não casou com nenhum dos padrões anteriores.

- Cara, até agora eu falei muito e bebi pouco. Agora eu vou te passar um exercício para você fazer em casa e me dar a
resposta da próxima vez que nos encontrarmos aqui no botequim, tá legal?

- Tá, mas antes informe ao pessoal que está acompanhando este curso conosco como eles podem te encontrar para fazer
críticas, contar piada, convidar para o chope, curso ou palestra ou até mesmo para falar mal dos políticos.

- É fácil, meu e-mail é julio.neves@gmail.com , mas pare de me embromar que eu não vou esquecer de te passar o
script para fazer. É o seguinte: quero que você faça um programa que receberá como parâmetro o nome de um arquivo e
que quando executado salvará este arquivo com o nome original seguido de um til (~) e colocará este arquivo dentro do
vi (o melhor editor que se tem notícia) para ser editado. Isso é para ter sempre a última cópia boa deste arquivo caso o
cara faça alterações indevidas. Obviamente, você fará as críticas necessárias, como verificar se foi passado um
parâmetro, se o arquivo passado existe, ... Enfim, o que te der na telha e você achar que deve constar do script. Deu prá
entender?

- Hum, hum...
- Chico! Traz mais um sem colarinho que o cara aqui já está dando para entender!

Parte V

Comandos de Loop (ou laços)

Muitos problemas requerem mecanismos de repetiçâo nos quais sequências de intruçôes precisam ser repetidas por várias vezes
usando conjuntos diferentes de dados. Mais comumente, uma seçâo de código que se repete é chamada de laço porque após a
execuçâo da última instruçâo o programa se bifurca e retorna à primeira instrução ou encerra a execução.
As instruçôes de loop ou laço que veremos sâo o for, o while e o until que veremos daqui em diante. Começaremos pelo laço for

O Comando for

Se você está acostumado a programar, certamente já conhece o comando for, mas o que você nâo sabe é que o for, que é uma
instruçâo instríseca 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.

Primeira sintaxe do comando for

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:

$ echo *

ArqDoDOS.txt1 confuso incusu logado musexc musicas musinc muslist

Isto é, o Shell viu o asterisco (*) 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:

$ cat testefor1
#!/bin/bash
# 1o. Prog didático para entender o for
for Arq in *
do
echo -n $Arq: # A opcao -n eh para nao saltar linha
done

Então vamos executá-lo:


$ testefor1 ArqDoDOS.txt1:confuso:incusu:logado:musexc:musicas:musinc:muslist:$

Como você viu o Shell transformou o asterísco (que odeia ser chamado de asterístico) em uma lista de arquivos separados por
espaços em branco. quando o 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):

$ cat testefor2 #!/bin/bash # 2o. Prog didático para entender o for


for Palavra in Papo de Botequim

do

echo $Palavra

done

E executando vem:

$ testefor2

Papo

de

Botequim

Como você viu, este exemplo é tão bobo e simples como o anterior, mas serve para mostrar o comportamento básico do 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:

$ echo "$IFS" | od -h

0000000 0920 0a0a

0000004

Isto é, mandei a variável (protegida da interpretação do Shell pelas aspas) para um dump hexadecimal (od
-h) e resultou:

Conteúdo da Variável $IFS


Hexadecimal Significado
09 <TAB>
20 <ESPAÇO>
0a <ENTER>

Onde o último 0a foi proveniente do <ENTER> dado ao final do comando. Para melhorar a explicação, vamos ver isso de outra
forma:

$ echo ":$IFS:" | cat -vet

: ^I$

:$

Preste atenção na dica a seguir para entender a construção deste comando cat:

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.
Agora veja você, 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:

$ cat musicas

album 1^Artista1~Musica1:Artista2~Musica2

album 2^Artista3~Musica3:Artista4~Musica4 album 3^Artista5~Musica5:Artista6~Musica6 album


4^Artista7~Musica7:Artista1~Musica3 album 5^Artista9~Musica9:Artista10~Musica10

Em cima deste "leiaute" foi desenvolvido o script a seguir

:$ cat listartista
#!/bin/bash
# Dado um artista, mostra as suas musicasif [ $# -ne 1 ]
then
echo Voce deveria ter passado um parametro
exit 1
fi
IFS="
:"
for ArtMus in $(cut -f2 -d^ musicas)
do
echo "$ArtMus" | grep $1 && echo $ArtMus | cut -f2 -d~
done

O script, como sempre, começa testando se os parâmetros foram passados corretamente, em seguida o 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:

$ listartista Artista1

Artista1~Musica1

Musica1

Artista1~Musica3

Musica3

Artista10~Musica10

Musica10

Êpa! Aconteceram duas coisas indesejáveis: os blocos também foram listados e a 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 .
Nesta 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^) de $ArtMus 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: que a cadeia está no início (cuja expressão regular é

$ cat listartista

#!/bin/bash

# Dado um artista, mostra as suas musicas

# versao 2
if [ $# -eq 0 ]

then

echo Voce deveria ter passado pelo menos um parametro

exit 1

fi
IFS="

:"
for ArtMus in $(cut -f2 -d^ musicas)
do
echo "$ArtMus" | grep -i "^$@~" > /dev/null && echo $ArtMus | cut -f2 -d~
done

Que executando vem:

$ listartista Artista1

Musica1

Musica3

Segunda sintaxe do comando for

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:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ cat listamusica
#!/bin/bash
# Recebe parte dos nomes de musicas como parametro e
# lista os interpretes. Se o nome for composto, deve
# ser passado entre aspas.
# ex. "Eu nao sou cachorro nao" "Churrasquinho de Mae"
#
if [ $# -eq 0 ]
then
echo Uso: $0 musica1 [musica2] ... [musican]
exit 1
fi
IFS="
:"
for Musica
do
echo $Musica
Str=$(grep -i "$Musica" musicas) ||
{
echo " Não encontrada"
continue
}
for ArtMus in $(echo "$Str" | cut -f2 -d^)
do
echo " $ArtMus" | grep -i "$Musica" | cut -f1 -d~
done
done

Da mesma forma que os outros, começamos o exercício com uma crítica sobre os parâmetros recebidos, em seguida fizemos um
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:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ listamusica musica3 Musica4 "Eguinha Pocotó"


musica3
Artista3
Artista1
Musica4
Artista4
Eguinha Pocotó
Não encontrada

A listagem ficou feinha porque ainda não sabemos formatar a saída, mas qualquer dia desses, quando você souber posicionar o
cursor, fazer negrito, trabalhar com cores e etc, faremos esta listagem novamente usando todas estas perfumarias e ela ficará
muito fashion.

A esta altura dos acontecimentos você deve estar se perguntando: "E aquele 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:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ for i in $(seq 9)
> do
> echo -n "$i "
> done
1 2 3 4 5 6 7 8 9

Neste a variável i assumiu os inteiros de 1 a 9 gerados pelo comando seq e a opção -necho 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: do

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ for i in $(seq 3 9)
> do
> echo -n "$i "
> done
4 5 6 7 8 9

Ou ainda na forma mais completa do seq:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ for i in $(seq 0 3 9)
> do
> echo -n "$i "
> done
0 3 6 9

2 – A outra forma de fazer o desejado é com uma sintaxe muito semelhante ao for da linguagem C, como veremos mais adiante.

Terceira sintaxe do comando for

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:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ for ((i=1; i<=9; i++))

> do

> echo -n "$i "

> done
1 2 3 4 5 6 7 8 9

Neste caso a variável 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 i1 a cada passada do loop. será de 1.

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.

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">
$ for ((; i<=9;))

> do

> let i++

> echo -n "$i "

> done
1 2 3 4 5 6 7 8 9

Repare que o incremento saiu do corpo do 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:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ echo $j

$ let j++
$ echo $j
1

Ou seja, a variável $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:

http://twiki.softwarelivre.org/pub/twiki/twikidocgraphics/pencil.gif) no-repeat scroll right top; -moz-background-clip: -moz-


initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; text-align: justify; color: rgb(0, 0,
0);">

$ for arq in *

> do

> let i++

> echo "$i -> $Arq"

> done
1 -> ArqDoDOS.txt1
2 -> confuso
3 -> incusu
4 -> listamusica
5 -> listartista
6 -> logado
7 -> musexc
8 -> musicas
9 -> musinc
10 -> muslist
11 -> testefor1
12 -> testefor2

- Pois é amigo, tenho certeza que você já tomou um xarope do comando 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.

Anda mungkin juga menyukai