Login
Estamos no Facebook
Buscar
Quem está conectado
Há 6 usuários online :: Nenhum usuário registrado, Nenhum Invisível e 6 Visitantes :: 2 Motores de buscaNenhum
[ Ver toda a lista ]
O recorde de usuários online foi de 468 em 1/3/2012, 10:43
Brasília
| |
Estamos no Twitter

Nossa Comunidade

Nosso Grupo

Últimos assuntos
Top dos mais postadores
| Marcos Guedes | ||||
| hugo | ||||
| alceu11 | ||||
| Julio | ||||
| m@r<3|o | ||||
| mfelis | ||||
| Tales Ruan | ||||
| Nelson Arcas | ||||
| _batmanvfp_ | ||||
| marcio |
Karaoke feito em FoxPro 2.6
23/5/2012, 11:45 por fabiomacarrao
Bom dia a todos. Desenvolvi um programa em FoxPro for windows 2.6 para karaoke. tenho mais de 2700 …
Comentários: 3
Estatísticas
Temos 4048 usuários registradosO último usuário registrado atende pelo nome de fabiomacarrao
Os nossos membros postaram um total de 14433 mensagens em 2047 assuntos
Debian, Shell Script (em andamento)
Página 1 de 1 • Compartilhe •
Debian, Shell Script (em andamento)
Este tuto ainda eh um rascunho
vou acertando ele aos poucos, por enquanto ai esta a primeira parte
Linux tem sido 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 sua interface. Essa interface da máquina é 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. Assim, 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: a linha de comando de sistemas Unix e Linux é mais poderosa.
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 "egg". É 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
[Você precisa estar registrado e conectado para ver este link.]
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 [Você precisa estar registrado e conectado para ver este link.] 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.
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:
Visão do shell em relação do Kernel do Linux
[Você precisa estar registrado e conectado para ver esta imagem.]
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.
Pinguim com placa de atenção 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
$ 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.
$ 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.
Pinguim com placa de dica Jamais Faça:
>$ 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:
>$ 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.
> $ 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:
[Você precisa estar registrado e conectado para ver este link.] 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 é:
[Você precisa estar registrado e conectado para ver este link.] 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.
continua
vou acertando ele aos poucos, por enquanto ai esta a primeira parte
Linux tem sido 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 sua interface. Essa interface da máquina é 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. Assim, 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: a linha de comando de sistemas Unix e Linux é mais poderosa.
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 "egg". É 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
[Você precisa estar registrado e conectado para ver este link.]
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 [Você precisa estar registrado e conectado para ver este link.] 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.
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:
Visão do shell em relação do Kernel do Linux
[Você precisa estar registrado e conectado para ver esta imagem.]
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.
Pinguim com placa de atenção 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
$ 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.
$ 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.
Pinguim com placa de dica Jamais Faça:
>$ 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:
>$ 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.
> $ 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:
[Você precisa estar registrado e conectado para ver este link.] 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 é:
[Você precisa estar registrado e conectado para ver este link.] 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.
continua
Última edição por hugo em 23/4/2010, 11:48, editado 1 vez(es)
_________________
"A tristeza é a falta de alegria, mais sem ela eu não poderia entender a alegria do fato de que a felicidade existe!"
Helio Leites - [Você precisa estar registrado e conectado para ver este link.]

hugo- Usuário 5 Estrelas

Re: Debian, Shell Script (em andamento)
Faça a seguinte experiência científica:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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!). smile
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção 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.
Pinguim com placa de atenção 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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 fim[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de atenção 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...
continua
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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!). smile
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção 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.
Pinguim com placa de atenção 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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 fim[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de atenção 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...
continua
_________________
"A tristeza é a falta de alegria, mais sem ela eu não poderia entender a alegria do fato de que a felicidade existe!"
Helio Leites - [Você precisa estar registrado e conectado para ver este link.]

hugo- Usuário 5 Estrelas

Re: Debian, Shell Script (em andamento)
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 (|).
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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 garconfrown
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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de atenção 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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ª
Valores Possíveis - r w x
- - s (suid)
-
Assim sendo, para descobrir todos os arquivos executáveis em um determinado diretório eu deveria fazer:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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):
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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!
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
[Você precisa estar registrado e conectado para ver este link.] 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,virando os olhos 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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção Então observe o que foi feito:
Delimitador do primeiro cut (
[Você precisa estar registrado e conectado para ver este link.] 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 (~)
[Você precisa estar registrado e conectado para ver este link.] 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 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
[Você precisa estar registrado e conectado para ver este link.] 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,virando os olhos 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.
[Você precisa estar registrado e conectado para ver este link.] 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
continua
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 (|).
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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 (
[Você precisa estar registrado e conectado para ver este link.] 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 (
Finalmente vamos ver o caso dos parênteses. Veja só o caso a seguir, bem parecido com o exemplo anterior:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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 garconfrown
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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de atenção 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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ª
Valores Possíveis - r w x
- - s (suid)
-
Assim sendo, para descobrir todos os arquivos executáveis em um determinado diretório eu deveria fazer:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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 (
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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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):
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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!
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
[Você precisa estar registrado e conectado para ver este link.] 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,virando os olhos 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.
[Você precisa estar registrado e conectado para ver este link.] 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 (
Então para pegarmos os dados referentes a todas as segundas músicas do arquivo musicas, devemos fazer:
[Você precisa estar registrado e conectado para ver este link.] 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 (
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção Então observe o que foi feito:
Delimitador do primeiro cut (
[Você precisa estar registrado e conectado para ver este link.] 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 (
Vamos então ver o que aconteceu no segundo cut:
Novo delimitador (~)
[Você precisa estar registrado e conectado para ver este link.] 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 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
[Você precisa estar registrado e conectado para ver este link.] 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,virando os olhos 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.
[Você precisa estar registrado e conectado para ver este link.] 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
continua
_________________
"A tristeza é a falta de alegria, mais sem ela eu não poderia entender a alegria do fato de que a felicidade existe!"
Helio Leites - [Você precisa estar registrado e conectado para ver este link.]

hugo- Usuário 5 Estrelas

Re: Debian, Shell Script (em andamento)
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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção Então observe o que foi feito:
Delimitador do primeiro cut (
[Você precisa estar registrado e conectado para ver este link.] 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 (~)
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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 sorriso) então, para entender o que o aluno havia feito, o chamei e em sua frente executei o seguinte comando:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
Pinguim com placa de dicaDica #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.
Pinguim com placa de dicaDica #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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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):
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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...
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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atençã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 egg 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):
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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, ...
Pergunta: Uma forma de testar se um diretório existe é fazendo o seguinte script. Caso não seja bem sucedido, é feito um agrupamento de comandos dentro das chaves, { }, orientando que seja criado um diretório e depois seja renomeado. Depois, finalmente, seria confirmado a existência do novo diretório. Caso já existisse o diretório, os comandos agrupados não seriam executados pelo fato de o conector utilizado ser do tipo 'ou', representado por ||.
cd aluno ||
{
mkdir aluno
mv aluno alunovo
cd alunovo
}
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
cadeia1 = padrao Verdadeiro se cadeia1 casa com padrão
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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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? smile )
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, poste as mensagens no forum de duvidas gerais, 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! smile
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.
Pergunta: O que acontece após a execução da última instrução dentro de um laço?
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:
Pinguim com placa de dica 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 realidadesorriso. 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
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 (
Então para pegarmos os dados referentes a todas as segundas músicas do arquivo musicas, devemos fazer:
[Você precisa estar registrado e conectado para ver este link.] 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 (
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção Então observe o que foi feito:
Delimitador do primeiro cut (
[Você precisa estar registrado e conectado para ver este link.] 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 (
Vamos então ver o que aconteceu no segundo cut:
Novo delimitador (~)
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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 sorriso) então, para entender o que o aluno havia feito, o chamei e em sua frente executei o seguinte comando:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
Pinguim com placa de dicaDica #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.
Pinguim com placa de dicaDica #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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atenção 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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):
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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...
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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
Pinguim com placa de atençã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 egg 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):
[Você precisa estar registrado e conectado para ver este link.] 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.
Pinguim com placa de dica 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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, ...
Pergunta: Uma forma de testar se um diretório existe é fazendo o seguinte script. Caso não seja bem sucedido, é feito um agrupamento de comandos dentro das chaves, { }, orientando que seja criado um diretório e depois seja renomeado. Depois, finalmente, seria confirmado a existência do novo diretório. Caso já existisse o diretório, os comandos agrupados não seriam executados pelo fato de o conector utilizado ser do tipo 'ou', representado por ||.
cd aluno ||
{
mkdir aluno
mv aluno alunovo
cd alunovo
}
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
cadeia1 = padrao Verdadeiro se cadeia1 casa com padrão
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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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 (;
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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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? smile )
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, poste as mensagens no forum de duvidas gerais, 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! smile
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.
Pergunta: O que acontece após a execução da última instrução dentro de um laço?
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 (
$ 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:
Pinguim com placa de dica 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 (
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 (
$ 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 realidadesorriso. 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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.
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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:
[Você precisa estar registrado e conectado para ver este link.] 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
_________________
"A tristeza é a falta de alegria, mais sem ela eu não poderia entender a alegria do fato de que a felicidade existe!"
Helio Leites - [Você precisa estar registrado e conectado para ver este link.]

hugo- Usuário 5 Estrelas

Página 1 de 1
Permissão deste fórum:
Você não pode responder aos tópicos neste fórum
Início
» Modificar TitleBar e Icone do Executavel.
» Criar atalho, SYS(2020) e Desktop
» Karaoke feito em FoxPro 2.6
» Como separar caminho do diretório?
» Utilizando PHPMailer
» Programador em Visual Foxpro
» Link PHP (Dúvida)
» Fundo do PROJETO Transparente??
» Minimizar , Maximizar e Restaurar
» Pivot Table no sql server
» Scroll EditBox Automatico
» Select Nexval do FoxPro no OracleXE
» Colocar gif na caixa do MESSAGEBOX ()
» Comparar Versões do programa.exe
» Menu lateral
» Fazer com que a tela do sistema assume a janela principal
» Trocar Palavra no Sistema
» invocar Dll em Xbase
» Fechar Porta Aberta