Escolar Documentos
Profissional Documentos
Cultura Documentos
Curso de Shell Script PDF
Curso de Shell Script PDF
Papo de Botequim
Voc no agenta mais aquele seu porque, em ingls, Shell significa con-
cha, carapaa, isto , fica entre o u-
amigo usurio de Linux enchendo o surio e o sistema operacional, de
forma que tudo que interage com
seu saco com aquela histria de que o o sistema operacional, tem que
passar pelo seu crivo.
sistema fantstico e o Shell uma
D
ilogo entreouvido em uma mesa O Linux, por definio, um sistema
de um botequim, entre um multiusurio no podemos nunca nos
usurio de Linux e um empur- esquecer disto e para permitir o acesso
rador de mouse: O ambiente Linux de determinados usurios e barrar a en-
Quem o Bash? Para voc entender o que e como fun- trada de outros, existe um arquivo cha-
o filho caula da famlia Shell. ciona o Shell, primeiro vou te mostrar mado /etc/passwd, que alm de fornecer
P cara! Ests a fim de me deixar como funciona o ambiente em camadas dados para esta funo de leo-de-ch-
maluco? Eu tinha uma dvida e voc do Linux. D uma olhada no grfico cara do Linux, tambm prov informa-
me deixa com duas! mostrado na Figura 1. es para o incio de uma sesso (ou
No, maluco voc j h muito tem- Neste grfico podemos ver que a ca- login, para os ntimos) daqueles que
po: desde que decidiu usar aquele sis- mada de hardware a mais profunda e passaram por esta primeira barreira. O
tema operacional que voc precisa formada pelos componentes fsicos do ltimo campo de seus registros informa
reiniciar dez vezes por dia e ainda por seu computador. Em torno dela, vem a ao sistema qual o Shell que a pessoa
cima no tem domnio nenhum sobre camada do kernel que o cerne do vai receber ao iniciar sua sesso.
o que esta acontecendo no seu com- Linux, seu ncleo, e quem pe o hard- Lembra que eu te falei de Shell, fa-
putador. Mas deixa isso pr l, pois ware para funcionar, fazendo seu geren- mlia, irmo? Pois , vamos comear a
vou te explicar o que Shell e os com- ciamento e controle. Os programas e entender isto: o Shell a conceituao
ponentes de sua famlia e ao final da comandos que envolvem o kernel, dele de concha envolvendo o sistema opera-
nossa conversa voc dir: Meu Deus se utilizam para realizar as tarefas para cional propriamente dito, o nome
do Shell! Porque eu no optei pelo que foram desenvolvidos. Fechando tudo genrico para tratar os filhos desta idia
Linux antes?. isso vem o Shell, que leva este nome que, ao longo dos muitos anos de exis-
maiores galhos. Seu nome pipe (que $ echo "Existem who | wc -l U $ (pwd ; cd /etc ; pwd)
em ingls significa tubo, j que ele cana- usuarios conectados" /home/meudir
liza a sada de um comando para a Existem who | wc -l usuarios U /etc
entrada de outro) e sua representao a conectados $ pwd
| (barra vertical). /home/meudir
Hi! Olha s, no funcionou! mesmo,
$ ls | wc -l no funcionou e no foi por causa das Quequeiiisso minha gente? Eu estava
21 aspas que eu coloquei, mas sim por que no /home/meudir, mudei para o /etc,
eu teria que ter executado o who | wc -l constatei que estava neste diretrio com
O comando ls passou a lista de arquivos antes do echo. Para resolver este proble- o pwd seguinte e quando o agrupamento
para o comando wc, que quando est ma, tenho que priorizar a segunda parte de comandos terminou, eu vi que conti-
com a opo -l conta a quantidade de li- do comando com o uso de crases: nuava no /etc/meudir!
nhas que recebeu. Desta forma, pode- Hi! Ser que tem coisa do mgico
mos afirmar categoricamente que no $ echo "Existem `who | wc -l` U Mandrake por a? Nada disso. O interes-
meu diretrio existiam 21 arquivos. usuarios conectados" sante do uso de parnteses que eles
Existem 8 usuarios U invocam um novo Shell para executar os
$ cat /etc/passwd | sort | lp conectados comandos que esto em seu interior.
Desta forma, fomos realmente para o
A linha de comandos acima manda a Para eliminar esse monte de brancos diretrio /etc, porm aps a execuo de
listagem do arquivo /etc/passwd para a antes do 8 que o wc -l produziu, basta todos os comandos, o novo Shell que
entrada do comando sort. Este a classi- retirar as aspas. Assim: estava no diretrio /etc morreu e retor-
fica e envia para o lp que o gerenciador namos ao Shell anterior que estava em
da fila de impresso. $ echo Existem `who | wc -l` U /home/meudir.
usuarios conectados Que tal usar nossos novos conceitos?
Caracteres de ambiente Existem 8 usuarios conectados
Quando queremos priorizar uma expres- $ mail suporte@linux.br << FIM
so, ns a colocamos entre parnteses, As aspas protegem da interpretao do Ola suporte, hoje as `date U
no ? Pois , por causa da aritmtica Shell tudo que est dentro dos seus lim- +%hh:mm` ocorreu novamente U
normal pensarmos deste jeito. Mas em ites. Como para o Shell basta um espao aquele problema que eu havia U
Shell o que prioriza mesmo so as crases em branco como separador, o monte de reportado por telefone. De U
(`) e no os parnteses. Vou dar exemp- espaos ser trocado por um nico aps acordo com seu pedido segue a U
los para voc entender melhor. a retirada das aspas. listagem do diretorio:
Eu quero saber quantos usurios esto Outra coisa interessante o uso do `ls -l`
logados no computador que eu admi- ponto-e-vrgula. Quando estiver no Shell, Abracos a todos.
nistro. Eu posso fazer: voc deve sempre dar um comando em FIM
cada linha. Para agrupar comandos em
$ who | wc -l uma mesma linha, temos que separ-los Finalmente agora podemos demonstrar o
8 por ponto-e-vrgula. Ento: que conversamos anteriormente sobre
here document. Os comandos entre
O comando who passa a lista de usurios $ pwd ; cd /etc; pwd ;cd -;pwd crases tem prioridade, portanto o Shell
conectados ao sistema para o comando /home/meudir os executar antes do redirecionamento
wc -l, que conta quantas linhas recebeu e /etc do here document. Quando o suporte
mostra a resposta na tela. Muito bem, /home/meudir receber a mensagem, ver que os
mas ao invs de ter um nmero oito comandos date e ls foram executados
solto na tela, o que eu quero mesmo Neste exemplo, listei o nome do diretrio antes do comando mail, recebendo ento
que ele esteja no meio de uma frase. Ora, corrente com o comando pwd, mudei um instantneo do ambiente no
para mandar frases para a tela eu s pre- para o diretrio /etc, novamente listei o momento de envio do email.
ciso usar o comando echo; ento vamos nome do diretrio e finalmente voltei pa- - Garom, passa a rgua!
ver como que fica: ra o diretrio onde estava anteriormente
(cd -), listando seu nome. Repare que Julio Cezar Neves Analista de Su-
SOBRE O AUTOR
Buraco Negro coloquei o ponto-e-vrgula de todas as porte de Sistemas desde 1969 e tra-
Em Unix existe um arquivo fantasma. formas possveis, para mostrar que no balha com Unix desde 1980, quando
Chama-se /dev/null.Tudo que enviado importa se existem espaos em branco fez parte da equipe que desenvolveu
para este arquivo some. Assemelha-se a um antes ou aps este caracter. o SOX, sistema operacional, similar
Buraco Negro. No caso do exemplo, como ao Unix, da Cobra Computadores.
Finalmente, vamos ver o caso dos
no me interessava guardar a possvel men- professor do curso de Mestrado em
parnteses. No exemplo a seguir, colo-
sagem de erro oriunda do comando rm, redi- Software Livre das Faculdades Estcio
camos diversos comandos separados por
recionei-a para este arquivo. de S, no Rio de Janeiro.
ponto-e-vrgula entre parnteses:
G
arom! Traz um chops e dois Olha que legal! O grep aceita como
pastel. O meu amigo hoje no entrada a sada de outro comando, redi-
vai beber porque est finalmente recionado por um pipe (isso muito
sendo apresentado a um verdadeiro sis- comum em Shell e um tremendo
tema operacional, e ainda tem muita acelerador da execuo de coman-
coisa a aprender! dos). Dessa forma, no 3 exemplo,
E ento, amigo, t entendendo o comando who listou as pessoas
tudo que te expliquei at agora? logadas na mesma mquina que
Entendendo eu t, mas no vi voc (no se esquea jamais: o
nada prtico nisso Linux multiusurio) e o grep foi
Calma rapaz, o que te falei at usado para verificar se o Carvalho
agora serve como base ao que h estava trabalhando ou coando.
de vir daqui pra frente. Vamos usar O grep um comando muito con-
essas ferramentas que vimos para hecido, pois usado com muita fre-
montar programas estruturados. Voc qncia. O que muitas pessoas no
ver porque at na TV j teve pro- sabem que existem trs comandos na
grama chamado O Shell o Limite. famlia grep: grep, egrep e fgrep. A princi-
Para comear vamos falar dos coman- pais diferenas entre os 3 so:
dos da famlia grep grep - Pode ou no usar expresses
Grep? No conheo nenhum termo em $ grep franklin /etc/passwd regulares simples, porm no caso de
ingls com este nome no us-las, o fgrep melhor, por ser
claro, grep um acrnimo (sigla) Pesquisando em vrios arquivos: mais rpido.
para Global Regular Expression Print, egrep (e de extended, estendido) -
que usa expresses regulares para $ grep grep *.sh muito poderoso no uso de expresses
pesquisar a ocorrncia de cadeias de regulares. Por ser o mais poderoso dos
caracteres na entrada definida. Pesquisando na sada de um comando: trs, s deve ser usado quando for
Por falar em expresses regulares (ou necessria a elaborao de uma
regexp), o Aurlio Marinho Jargas es- $ who | grep carvalho expresso regular no aceita pelo grep.
creveu dois artigos [1 e 2] imperdveis fgrep (f de fast, rpido) - Como o
para a Revista do Linux sobre esse No 1 exemplo, procurei a palavra nome diz, o ligeirinho da famlia,
assunto e tambm publicou um livro [3] franklin em qualquer lugar do arquivo executando o servio de forma muito
pela Editora Novatec. Acho bom voc ler /etc/passwd. Se quisesse procurar um veloz (por vezes cerca de 30% mais
esses artigos, eles vo te ajudar no que nome de usurio, isto , somente no in- rpido que o grep e 50% mais que o
est para vir. cio dos registros desse arquivo, poderia egrep), porm no permite o uso de
digitar $ grep ^franklin /etc/passwd. expresses regulares na pesquisa.
Eu fico com grep,voc com gripe E para que servem o circunflexo e os Agora que voc j conhece as difer-
Esse negcio de gripe brincadeira, s apstrofos?, voc vai me perguntar. Se enas entre os membros da famlia, me
um pretexto para pedir umas caipirinhas. tivesse lido os artigos que mencionei, diga: o que voc acha dos trs exemplos
Eu te falei que o grep procura cadeias de saberia que o circunflexo serve para limi- que eu dei antes das explicaes?
caracteres dentro de uma entrada defi- tar a pesquisa ao incio de cada linha e Achei que o fgrep resolveria o teu prob-
nida, mas o que vem a ser uma entrada os apstrofos servem para o Shell no lema mais rapidamente que o grep.
definida? Bem, existem vrias formas interpretar esse circunflexo, deixando-o Perfeito! T vendo que voc est
de definir a entrada do comando grep. passar inclume para o comando grep. atento, entendendo tudo que estou te
Veja s. Para pesquisar em um arquivo: No 2 exemplo mandei listar todas as explicando! Vamos ver mais exemplos
para clarear de vez as diferenas de -l). Os apstrofos foram usados para o Isto , o nome do lbum ser separado
uso entre os membros da famlia. Shell no ver o circunflexo. Vamos ver por um circunflexo do resto do registro,
Eu sei que em um arquivo qualquer mais um. Veja na Tabela 1 as quatro formado por diversos grupos compostos
existe um texto falando sobre Linux, s primeiras posies possveis da sada de pelo intrprete de cada msica do CD e a
no tenho certeza se est escrito com L um ls -l em um arquivo comum (no msica interpretada. Estes grupos so
maisculo ou minsculo. Posso fazer diretrio, nem link, nem ). separados entre si por dois pontos (:) e,
uma busca de duas formas: Para descobrir todos os arquivos exe- internamente, o intrprete ser separado
cutveis em um determinado diretrio por um til (~) do nome da msica.
egrep (Linux | linux) arquivo.txt eu poderia fazer: Quero escrever um programa chamado
musinc, que incluir registros no meu
ou ento: $ ls -la | egrep ^-..(x|s) arquivo msicas. Passarei cada lbum
como parmetro para o programa:
grep [Ll]inux arquivo.txt novamente usamos o circunflexo para
limitar a pesquisa ao incio de cada $ musinc lbum^interprete~U
No primeiro caso, a expresso regular linha, ou seja, listamos as linhas que musica:interprete~musica:...
complexa (Linux | linux) usa os parnte- comeam por um trao (-), seguido de
ses para agrupar as opes e a barra ver- qualquer coisa (o ponto), novamente Desta forma, musinc estar recebendo os
tical (|) usada como um ou (or, em seguido de qualquer coisa, e por fim um dados de cada lbum como se fosse uma
ingls) lgico, isto , estou procurando x ou um s. Obteramos o mesmo resul- varivel. A nica diferena entre um
Linux ou linux. tado se usssemos o comando: parmetro recebido e uma varivel que
No segundo, a expresso regular os primeiros recebem nomes numricos
[Ll]inux significa: comeado por L ou l $ ls -la | grep ^-..[xs] (o que quis dizer que seus nomes so
seguido de inux. Como esta uma formados somente por um algarismo,
expresso simples, o grep consegue e alm disso, agilizaramos a pesquisa. isto , $1, $2, $3, , $9). Vamos, fazer
resolv-la, por isso melhor usar a mais alguns testes:
segunda forma, j que o egrep tornaria a A CDteca
pesquisa mais lenta. Vamos comear a desenvolver progra- $ cat teste
Outro exemplo. Para listar todos os mas! Creio que a montagem de um #!/bin/bash
subdiretrios do diretrio corrente, basta banco de dados de msicas bacana #Teste de passagem de parametros
usar o comando $ ls -l | grep ^d. Veja o para efeito didtico (e til nestes tempos echo 1o. parm -> $1
resultado no Quadro 1. de downloads de arquivos MP3 e echo 2o. parm -> $2
No exemplo, o circunflexo (^) serviu queimadores de CDs). No se esquea echo 3o. parm -> $3
para limitar a pesquisa primeira que, da mesma forma que vamos desen-
posio da sada do ls longo (parmetro volver um monte de programas para Agora vamos rodar esse programinha:
organizar os seus CDs de msica, com
Tabela 1 pequenas adaptaes voc pode fazer o $ teste passando parametros para U
mesmo para organizar os CDs de soft- testar
Posio Valores possveis
1 -
ware que vm com a Linux Magazine e bash: teste: cannot execute
2 r ou -
outros que voc compra ou queima, e
3 w ou - disponibilizar esse banco de software Ops! Esqueci-me de tornar o script exe-
4 x,s(suid) ou - para todos os que trabalham com voc cutvel. Vou fazer isso e testar nova-
(o Linux multiusurio, e como tal deve mente o programa:
$ chmod 755 teste Execute o programa: incluso de CDs no meu banco chamado
$ teste passando parametros para U musicas. O programa muito simples
testar $ teste passando parametros para testar (como tudo em Shell). Veja a Listagem 1.
1o. parm -> passando O programa teste recebeu 4 U O script simples e funcional; limito-
2o. parm -> parametros parametros me a anexar ao fim do arquivo musicas o
3o. parm -> para 1o. parm -> passando parmetro recebido. Vamos cadastrar 3
2o. parm -> parametros lbuns para ver se funciona (para no
Repare que a palavra testar, que seria o 3o. parm -> para ficar enchendo lingia, suponho que
quarto parmetro, no foi listada. Isso Para listar todos de uma U em cada CD s existem duas msicas):
ocorreu porque o programa teste s lista tacada eu faco passando U
os trs primeiros parmetros recebidos. parametros para testar $ musinc album3^Artista5U
Vamos execut-lo de outra forma: ~Musica5:Artista6~Musica5
Repare que antes das aspas usei uma $ musinc album1^Artista1U
$ teste passando parametros U barra invertida, para escond-las da ~Musica1:Artista2~Musica2
para testar interpretao do Shell (se no usasse as $ musinc album 2^Artista3U
1o. parm -> passando parametros contrabarras as aspas no apareceriam). ~Musica3:Artista4~Musica4
2o. parm -> para Como disse, os parmetros recebem
3o. parm -> testar nmeros de 1 a 9, mas isso no significa Listando o contedo do arquivo musicas:
que no posso usar mais de nove
As aspas no deixaram o Shell ver o parmetros. Significa que s posso $ cat musicas
espao em branco entre as duas enderear nove. Vamos testar isso: album3^Artista5~Musica5:Artista6U
primeiras palavras, e elas foram consid- ~Musica6
eradas como um nico parmetro. E $ cat teste album1^Artista1~Musica1:Artista2U
falando em passagem de parmetros, #!/bin/bash ~Musica2
uma dica: veja na Tabela 2 algumas var- # Programa para testar passagem U album2^Artista3~Musica3:Artista4U
iveis especiais. Vamos alterar o pro- de parametros (3a. Versao) ~Musica4
grama teste para usar as novas variveis: echo O programa $0 recebeu $# U
parametros Podia ter ficado melhor. Os lbuns esto
$ cat teste echo 11o. parm -> $11 fora de ordem, dificultando a pesquisa.
#!/bin/bash shift Vamos alterar nosso script e depois test-
# Programa para testar passagem U echo 2o. parm -> $1 lo novamente. Veja a listagem 2. Sim-
de parametros (2a. Versao) shift 2 plesmente inseri uma linha que classifica
echo O programa $0 recebeu $# U echo 4o. parm -> $1 o arquivo musicas, redirecionando a
parametros sada para ele mesmo (para isso serve a
echo 1o. parm -> $1 Execute o programa: opo -o), aps cada lbum ser anexado.
echo 2o. parm -> $2
echo 3o. parm -> $3 $ teste passando parametros para U $ cat musicas
echo Para listar todos de uma U testar album1^Artista1~Musica1:Artista2U
\tacada\ eu faco $* O programa teste recebeu 4 U ~Musica2
parametros que so: albu2^Artista3~Musica3:Artista4U
Listagem 1: Incluindo CDs 11o. parm -> passando1 ~Musica4
na CDTeca 2o. parm -> parametros album3^Artista5~Musica5:Artista6U
4o. parm -> testar ~Musica6
$ cat musinc
#!/bin/bash Duas coisas muito interessantes aconte- Oba! Agora o programa est legal e
# Cadastra CDs (versao 1) ceram neste script. Para mostrar que os quase funcional. Ficar muito melhor em
# nomes dos parmetros variam de $1 a $9 uma nova verso, que desenvolveremos
echo $1 >> musicas digitei echo $11 e o que aconteceu? O aps aprender a adquirir os dados da tela
Shell interpretou como sendo $1 seguido e formatar a entrada.
do algarismo 1 e listou passando1;
Listagem 2 O comando shift, cuja sintaxe shift n, Tabela 2: Variveis especiais
podendo o n assumir qualquer valor
$ cat musinc Varivel Significado
numrico, despreza os n primeiros
#!/bin/bash $0 Contm o nome do programa
parmetros, tornando o parmetro de
# Cadastra CDs (versao 2) $# Contm a quantidade de
ordem n+1. parmetros passados
#
Bem, agora que voc j sabe sobre $* Contm o conjunto de todos os
echo $1 >> musicas
passagem de parmetros, vamos voltar parmetros (muito parecido com $@)
sort -o musicas musicas
nossa cdteca para fazer o script de
Analista de Suporte de
Listagem 4 ~Musica2
Sistemas desde 1969 e
muslist melhorado album3^Artista5~Musica5:Artista6U trabalha com Unix
~Musica6 desde 1980, quando
$ cat muslist
fez parte da equipe
#!/bin/bash
Conforme expliquei antes, o grep do que desenvolveu o
# Consulta CDs (versao 2) SOX, um sistema
exemplo listou todos os registros de
# operacional similar ao Unix, produzido
musicas exceto o referente a album 2, pela Cobra Computadores.
grep -i $1 musicas
porque atendia ao argumento do co-
Papo de
botequim III
Um chopinho, um aperitivo e o papo continua. Desta vez vamos aprender
alguns comandos de manipulao de cadeias de caracteres, que sero muito
usado para cortar um determinado circunexo (^) do resto do registro, que Para entender melhor isso, vamos anali-
pedao de um arquivo. A sintaxe e formado por diversos grupos compos- sar a primeira linha de musicas:
alguns exemplos de uso podem ser vis- tos pelo intrprete de cada msica do
tos no Quadro 1: CD e a respectiva msica interpretada. $ head -1 musicas
Como d para ver, existem quatro Estes grupos so separados entre si por album 1^Artista1~Musica1: U
sintaxes distintas: na primeira (-c 1-5) dois-pontos (:) e o intrprete ser sepa- Artista2~Musica2
especifiquei uma faixa, na segunda rado do nome da msica por um til (~).
(-c -6) especifiquei todo o texto at Ento, para pegar os dados referentes Ento observe o que foi feito:
uma posio, na terceira (-c 4-) tudo de a todas as segundas msicas do arquivo
uma determinada posio em diante e musicas, devemos digitar: album 1^Artista1~Musica1: U
na quarta (-c 1,3,5,7,9), s as posies Artista2~Musica2
determinadas. A ltima possibilidade $ cut -f2 -d: musicas
(-c -3,5,8-) foi s para mostrar que pode- Artista2~Musica2 Desta forma, no primeiro cut o primeiro
mos misturar tudo. Artista4~Musica4 campo do delimitador (-d) dois-pon-
Mas no pense que acabou por a! Artista6~Musica5 tos (:) album 1^Artista1~Musica1 e o
Como voc deve ter percebido, esta Artista8~Musica8@10_L: segundo, que o que nos interessa,
forma de cut muito til para lidar com Artista2~Musica2. Vamos ento ver o
arquivos com campos de tamanho xo, Ou seja, cortamos o segundo campo que aconteceu no segundo cut:
mas atualmente o que mais existe so z(-f de eld, campo em ingls) delimi-
arquivos com campos de tamanho vari- tado (-d) por dois-pontos (:). Mas, se Artista2~Musica2
vel, onde cada campo termina com um quisermos somente os intrpretes, deve-
delimitador. Vamos dar uma olhada no mos digitar: Agora, primeiro campo do delimitador
arquivo musicas que comeamos a pre- (-d) til (~), que o que nos interessa,
parar na ltima vez que viemos aqui no $ cut -f2 -d: musicas | cut U Artista2 e o segundo Musica2. Se
botequim. Veja o Quadro 2. -f1 -d~ o raciocnio que fizemos para a pri-
www.linuxmagazine.com.br Outubro 2004 85
Mas h duas semanas ocorreria o Bem a opo -d do tr remove do arquivo E veja tambm o date:
seguinte: todas as ocorrncias do caractere espe-
cificado. Desta forma eu removi os $ date
$ date | cut -f 4 -d caracteres indesejados, salvei o texto Mon Sep 20 10:47:19 BRT 2004
5 em um arquivo temporrio e posterior-
mente renomeei-o para o nome original. Repare que o ms e o dia esto no
Isto porque existem 2 caracteres em Uma observao: em um sistema Unix mesmo formato em ambos os coman-
branco antes do 5 (dia). Ento o ideal eu deveria digitar: dos. Ora, se em algum registro do who
seria transformar os espaos em branco eu no encontrar a data de hoje, sinal
consecutivos em somente um espao $ tr -d \015 < ArqDoDOS.U que o usurio est logado h mais
para poder tratar os dois resultados do txt > /tmp/$$ de um dia, j que ele no pode ter se
comando date da mesma forma, e isso logado amanh Ento vamos guar-
se faz assim: Uma dica: o problema com os termina- dar o pedao que importa da data de
dores de linha (CR/LF) s aconteceu hoje para depois procur-la na sada do
$ date | tr -s porque a transferncia do arquivo foi comando who:
Sun Sep 5 10:12:33 2004 feita no modo binrio (ou image), Se
antes da transmisso do arquivo tivesse $ Data=$(date | cut -f 2-3 U
E agora eu posso cortar: sido estipulada a opo ascii do ftp, isto -d )
no teria ocorrido.
$ date | tr -s | cut -f 4 U Olha, depois desta dica t comeando a Eu usei a construo $(...), para prio-
-d gostar deste tal de shell, mas ainda tem rizar a execuo dos comandos antes
10:12:33 muita coisa que no consigo fazer. de atribuir a sua sada varivel Data.
Pois , ainda no te falei quase nada Vamos ver se funcionou:
Olha s como o Shell est quebrando o sobre programao em shell, ainda
galho. Veja o contedo de um arquivo tem muita coisa para aprender, mas $ echo $Data
baixado de uma mquina Windows: com o que aprendeu, j d para resol- Sep 20
ver muitos problemas, desde que voc
$ cat -ve ArqDoDOS.txt adquira o modo shell de pensar. Voc Beleza! Agora, o que temos que fazer
Este arquivo^M$ seria capaz de fazer um script que diga procurar no comando who os registros
foi gerado pelo^M$ quais pessoas esto logadas h mais que no possuem esta data.
DOS/Win e foi^M$ de um dia no seu servidor?
baixado por um^M$ Claro que no! Para isso seria necess- Ah! Eu acho que estou entendendo!
ftp mal feito.^M$ rio eu conhecer os comandos condicio- Voc falou em procurar e me ocorreu o
nais que voc ainda no me explicou comando grep, estou certo?
Dica: a opo -v do cat mostra os carac- como funcionam. - Deixa eu tentar Certssimo! S que eu tenho que usar o
teres de controle invisveis, com a nota- mudar um pouco a sua lgica e traz- grep com aquela opo que ele s lista
o ^L, onde ^ a tecla Control e L la para o modo shell de pensar, mas os registros nos quais ele no encon-
a respectiva letra. A opo -e mostra o antes melhor tomarmos um chope. trou a cadeia. Voc se lembra que
nal da linha como um cifro ($). Agora que j molhei a palavra, vamos opo essa?
Isto ocorre porque no DOS o m dos resolver o problema que te propus. Veja Claro, a -v
registros indicado por um Carriage como funciona o comando who: Isso! T cando bom! Vamos ver:
Return (\r Retorno de Carro, CR) e
um Line Feed (\f Avano de Linha, ou $ who $ who | grep -v $Data
LF). No Linux porm o nal do regis- jneves pts/ U jneves pts/ U
tro indicado somente pelo Line Feed. 1 Sep 18 13:40 1 Sep 18 13:40
Vamos limpar este arquivo: rtorres pts/ U
0 Sep 20 07:01 Se eu quisesse um pouco mais de perfu-
$ tr -d \r < ArqDoDOS.txt > /tmp/$$ rlegaria pts/ U maria eu faria assim:
$ mv -f /tmp/$$ ArqDoDOS.txt 1 Sep 20 08:19
lcarlos pts/ U $ who | grep -v $Data |U
Agora vamos ver o que aconteceu: 3 Sep 20 10:01 cut -f1 -d
jneves
$ cat -ve ArqDoDOS.txt Tabela 1 O comando tr
Este arquivo$ Opo Signicado Viu? No foi necessrio usar comando
foi gerado pelo$ condicional, at porque o nosso
-s Comprime n ocorrncias de
DOS/Rwin e foi$ cadeia1 em apenas uma
comando condicional, o famoso if, no
baixado por um$ testa condio, mas sim instrues.
-d Remove os caracteres de cadeia1
ftp mal feito.$ Mas antes veja isso:
www.linuxmagazine.com.br Outubro 2004 87
Lembra que eu falei que ao longo dos no incio da cadeia e cifro ($) no m, Como voc viu, o programa melho-
nossos papos e chopes os nossos pro- servem para testar se o parmetro (o rou um pouquinho, mas ainda no est
gramas iriam se aprimorando? Ento lbum e seus dados) exatamente igual pronto. medida que eu te ensinar a
vejamos agora como podemos melhorar a algum registro j existente. Vamos programar em shell, nossa CDteca vai
o nosso programa para incluir msicas executar nosso programa novamente, car cada vez melhor.
na "CDTeca": mas desta vez passamos como parme- Entendi tudo que voc me explicou,
tro um lbum j cadastrado, pra ver o mas ainda no sei como fazer um if
$ cat musinc que acontece: para testar condies, ou seja o uso
#!/bin/bash normal do comando.
# Cadastra CDs (versao 3) $ musinc album 4^Artista7~ U Para isso existe o comando test, que
# Musica7:Artista8~Musica8 testa condies. O comando if testa
if grep ^$1$ musicas > U Este lbum j est cadastrado o comando test. Como j falei muito,
/dev/null preciso de uns chopes para molhar a
then E agora um no cadastrado: palavra. Vamos parar por aqui e na
echo Este lbum j est U prxima vez te explico direitinho o
cadastrado $ musinc album 5^Artista9~ U uso do test e de diversas outras sinta-
else Musica9:Artista10~Musica10 xes do if.
echo $1 >> musicas $ cat musicas Falou! Acho bom mesmo porque eu
sort musicas -o musicas album 1^Artista1~Musica1: U tambm j t cando zonzo e assim
Artista2~Musica2 tenho tempo para praticar esse monte
album 2^Artista3~Musica3: U de coisas que voc me falou hoje.
Como voc viu, uma pequena evolu- Artista4~Musica4 Para xar o que voc aprendeu, tente
o em relao verso anterior. Antes album 3^Artista5~Musica5: U fazer um scriptizinho para informar se
de incluir um registro (que na verso Artista6~Musica5 um determinado usurio, cujo nome
anterior poderia ser duplicado), testa- album 4^Artista7~Musica7: U ser passado como parmetro, est
mos se o registro comea (^) e termina Artista8~Musica8 logado no sistema ou no.
($) de forma idntica ao parmetro album 5^Artista9~Musica9: U A Chico! traz mais dois chopes pra
lbum passado ($1). O circunexo (^) Artista10~Musica10 mim por favor
www.linuxmagazine.com.br Outubro 2004 89
Papo de
botequim IV
O garon j perdeu a conta das cervejas, e o assunto no acaba. Desta
Dave Hamilton - www.sxc.hu
84 edio 04 www.linuxmagazine.com.br
www.linuxmagazine.com.br edio 04 85
sobre a -o e, dessa, forma o que foi ava- Da mesma forma, para escolhermos mente a legibilidade, pois o comando if
liado primeiro foi a expresso: CDs que tenham a participao do ir car com a sintaxe semelhante das
Artista1 e do Artista2, no necess- outras linguagens; por isso, esse ser o
$Familia = canideo -a $Genero = U rio montar um if com o conector -o. O modo como o comando test ser usado
ona egrep tambm resolve isso para ns. daqui para a frente.
Veja como: Se voc pensa que acabou, est muito
Que foi avaliada como falsa, retor- enganado. Preste ateno Tabela Ver-
nando o seguinte: $ egrep (Artista1|Artista2) U dade na Tabela 5.
musicas
$Familia = felino -o FALSO -o U Tabela 5 - Tabela Verdade
$Genero = lobo Ou (nesse caso especco) o prprio
Combinao E OU
grep poderia nos quebrar o galho:
VERDADEIRO-VERDADEIRO TRUE TRUE
Que resolvida resulta em:
VERDADEIRO-FALSO FALSE TRUE
$grep Artista[12] musicas FALSO-VERDADEIRO FALSE TRUE
VERDADEIRO -o FALSO -o FALSO FALSO-FALSO FALSE FALSE
No egrep acima, foi usada uma
Como agora todos os conectores so expresso regular, na qual a barra ver-
-o, e para que uma srie de expresses tical (|) trabalha como um ou lgico e Ou seja, quando o conector e e a
conectadas entre si por diversos ou os parnteses so usados para limitar primeira condio verdadeira, o resul-
lgicos seja verdadeira, basta que uma a amplitude deste ou. J no grep da tado nal pode ser verdadeiro ou falso,
delas o seja. A expresso nal resultou linha seguinte, a palavra Artista deve dependendo da segunda condio; j no
como VERDADEIRO e o then foi exe- ser seguida por um dos valores da lista conector ou, caso a primeira condio
cutado de forma errada. Para que isso formada pelos colchetes ([]), isto , 1 ou 2. seja verdadeira, o resultado sempre ser
volte a funcionar faamos o seguinte: verdadeiro. Se a primeira for falsa, o resul-
T legal, eu aceito o argumento, o if tado depender da segunda condio.
$ if test \($Familia = felino U do shell muito mais poderoso que Ora, os caras que desenvolveram o
-o $Familia = canideo\) -a U os outros caretas - mas, c entre ns, interpretador no so bobos e esto
\($Genero = ona -o $Genero = U essa construo de if test ... muito sempre tentando otimizar ao mximo
lobo\) esquisita, pouco legvel. os algoritmos. Portanto, no caso do
> then , voc tem razo, eu tambm no conector e, a segunda condio no ser
> echo Cuidado gosto disso e acho que ningum gosta. avaliada, caso a primeira seja falsa, j
> else Acho que foi por isso que o shell que o resultado ser sempre falso. J
> echo Pode passar a mo incorporou outra sintaxe, que substi- com o ou, a segunda ser executada
> tui o comando test. somente caso a primeira seja falsa.
Pode passar a mo Aproveitando-se disso, uma forma
Para isso vamos pegar aquele exem- abreviada de fazer testes foi criada. O
Desta forma, com o uso dos parn- plo para fazer uma troca de diretrios, conector e foi batizado de && e o ou de
teses agrupamos as expresses com o que era assim: ||. Para ver como isso funciona, vamos
conector -o, priorizando a execuo e us-los como teste no nosso velho
resultando em VERDADEIRO -a FALSO. if test ! -d lmb exemplo de troca de diretrio, que em
Para que seja VERDADEIRO o resul- then sua ltima verso estava assim:
tado de duas expresses ligadas pelo mkdir lmb
conector -a, necessrio que ambas if [ ! -d lmb ]
sejam verdadeiras, o que no o caso cd lmb then
do exemplo acima. Assim, o resultado mkdir lmb
nal foi FALSO, sendo ento o else cor- e utilizando a nova sintaxe, vamos faz-
retamente executado. lo assim: cd lmb
Se quisermos escolher um CD que
tenha faixas de 2 artistas diferentes, nos if [ ! -d lmb ] O cdigo acima tambm poderia ser
sentimos tentados a usar um if com o then escrito de maneira abreviada:
conector -a, mas sempre bom lembrar mkdir lmb
que o bash nos oferece muitos recursos [ ! -d lmb ] && mkdir lmb
e isso poderia ser feito de forma muito cd lmb cd dir
mais simples com um nico comando
grep, da seguinte forma: Ou seja, o comando test pode ser Tambm podemos retirar a negao (!):
substitudo por um par de colchetes ([]),
$ grep Artista1 musicas | grep U separados por espaos em branco dos [ -d lmb ] || mkdir lmb
Artista2 argumentos, o que aumentar enorme- cd dir
86 edio 04 www.linuxmagazine.com.br
Tabela 6 diretrio para dentro dele. Para execu- sempre estar dentro de lmb, desde que
tar mais de um comando dessa forma, tenha permisso para entrar neste dire-
Caractere Signicado
necessrio fazer um grupamento de trio, permisso para criar um subdire-
* Qualquer caractere ocorrendo zero ou
comandos, o que se consegue com o trio dentro de ../lmb, que haja espao
mais vezes
? Qualquer caractere ocorrendo uma vez
uso de chaves ({}). Veja como seria o em disco suciente...
[...] Lista de caracteres modo correto: Vejamos um exemplo didtico: depen-
| ou lgico dendo do valor da varivel $opc o script
cd lmb || dever executar uma das opes a
{ seguir: incluso, excluso, alterao ou
No primeiro caso, se o primeiro mkdir lmb encerrar sua execuo. Veja como ca-
comando (o test, que est represen- cd lmb ria o cdigo:
tado pelos colchetes) for bem sucedido, }
isto , se o diretrio lmb no existir, o if [ $opc -eq 1 ]
comando mkdir ser executado porque Ainda no est legal porque, caso o then
a primeira condio era verdadeira e o diretrio no exista, o cd exibir uma inclusao
conector era e. mensagem de erro. Veja o modo certo: elif [ $opc -eq 2 ]
No exemplo seguinte, testamos se o then
diretrio lmb existia (no anterior testa- cd lmb 2> /dev/null || exclusao
mos se ele no existia) e, caso isso fosse { elif [ $opc -eq 3 ]
verdade, o mkdir no seria executado mkdir lmb then
porque o conector era ou. Outra forma cd lmb alteracao
de escrever o programa: } elif [ $opc -eq 4 ]
then
cd lmb || mkdir lmb Como voc viu, o comando if nos per- exit
mitiu fazer um cd seguro de diversas else
Nesse caso, se o comando cd fosse maneiras. sempre bom lembrar que o echo Digite uma opo entre U
mal sucedido, o diretrio lmb seria seguro a que me re ro diz respeito ao 1 e 4
criado mas no seria feita a mudana de fato de que ao nal da execuo voc
www.linuxmagazine.com.br edio 04 87
88 edio 04 www.linuxmagazine.com.br
Papo de
Botequim V
Blocos de cdigo e laos (ou loops, como preferem alguns)
Dave Hamilton - www.sxc.hu
F ala cara! E as idias esto em
ordem? J fundiu a cuca ou voc
ainda agenta mais Shell?
At agora j vimos alguns blocos de
cdigo, como quando te mostrei um
exemplo para fazer um cd para dentro
Gento! T gostando muito! Gostei de um diretrio:
tanto que at caprichei no exerccio
Quadro 1: vira.sh
$ cat vira.sh
#!/bin/bash
#
que voc passou. Lembra que voc cd lmb 2> /dev/null || # vira - vi resguardando
me pediu para fazer um programa { # arquivo anterior
que recebe como parmetro o nome mkdir lmb # Verica se algum parmetro foi
de um arquivo e que quando execu- cd lmb # passado
tado salva esse arquivo com o nome } if [ $# -ne 1 ]
original seguido de um til (~) e o then
abre dentro do vi? O fragmento contido entre as duas echo Erro -> Uso: $0 U
Claro que lembro, me mostre e expli- chaves ({}) forma um bloco de cdigo. <arquivo>
que como voc fez. Tambm nesse exerccio que acabamos exit 1
Beleza, d uma olhada no quadro 1 de ver, em que salvamos o arquivo antes
, beleza! Mas me diz uma coisa: por de edit-lo, existem vrios blocos de Arq=$1
que voc terminou o programa com cdigo compreendidos entre os coman- # Caso o arquivo no exista, no
um exit 0? dos then e do if. Um bloco de cdigo # h cpia a ser salva
Eu descobri que o nmero aps o tambm pode estar dentro de um case if [ ! -f $Arq ]
exit indica o cdigo de retorno do ou entre um do e um done. then
programa (o $?, lembra?) e assim, Pera, Julio, que do e done so esses? vi $Arq
como a execuo foi bem sucedida, No me lembro de voc ter falado exit 0
ele encerra com o $?=0. Porm, se nisso, e olha que estou prestando
voc observar, ver que caso o pro- muita ateno... # Se eu no puder alterar o
grama no tenha recebido o nome do Pois , ainda no tinha falado porque #arquivo, vou usar o vi para que?
arquivo ou caso o operador no tenha no havia chegado a hora certa. if [ ! -w $Arq ]
permisso de gravao nesse arquivo, Todas as instrues de loop ou lao then
o cdigo de retorno ($?) seria dife- executam os comandos do bloco com- echo Voc no tem permisso U
rente do zero. preendidos entre um do e um done. As de escrita em $Arq
Grande garoto, aprendeu legal, mas instrues de loop ou lao so for, while exit 2
bom deixar claro que exit 0, simples- e until , que sero explicadas uma a
mente exit ou no colocar exit produ- uma a partir de hoje. # J que est tudo OK, vou
zem igualmente um cdigo de retorno # salvar a cpia e chamar o vi
($?) igual a zero. Agora vamos falar O comando For cp -f $Arq $Arq~
sobre as instrues de loop ou lao, Se voc est habituado a programar, vi $Arq
mas antes vou passar o conceito de certamente j conhece o comando for, exit 0
bloco de cdigo. mas o que voc no sabe que o for,
www.linuxmagazine.com.br edio 05 89
que uma instruo intrnseca do Shell Ento vamos execut-lo: $ echo $IFS | od -h
(isso signica que o cdigo fonte do 0000000 0920 0a0a
comando faz parte do cdigo fonte do $ testefor1 0000004
Shell, ou seja, em bom programs um ArqDoDOS.txt1:confuso:incusu:
built-in), muito mais poderoso que os logado:musexc:musicas:musinc: Isto , mandei a varivel (protegida
seus correlatos das outras linguagens. muslist:$ da interpretao do Shell pelas aspas)
Vamos entender a sua sintaxe, pri- para um dump hexadecimal (od -h). O
meiro em portugus e, depois, como Como voc viu, o Shell transformou resultado pode ser interpretado com a
funciona pra valer. Olhe s: o asterisco (que odeia ser chamado tabela abaixo:
de asterstico) em uma lista de arqui-
para var em val1 val2 ... valn vos separados por espaos em branco. Tabela 1: Resultado do od -h
faa Quando o for viu aquela lista, disse:
cmd1 Opa, listas separadas por espaos Valor Hexadecimal Signicado
90 edio 05 www.linuxmagazine.com.br
www.linuxmagazine.com.br edio 05 91
Da mesma forma que os outros, come- Ou na forma mais completa do seq: Repare que o incremento saiu do
amos o exerccio com uma crtica sobre corpo do for e passou para o bloco de
os parmetros recebidos, em seguida for i in $(seq 0 3 9) cdigo; repare tambm que, quando
fizemos um for em que a varivel do usei o let, no foi necessrio iniciali-
$Musica receber cada um dos parme- echo -n $i zar a varivel $i. Veja s os comandos a
tros passados, colocando em $Str todos done seguir, digitados diretamente no prompt,
os lbuns que contm as msicas dese- 0 3 6 9 para demonstrar o que acabo de falar:
jadas. Em seguida, o outro for pega cada
bloco Artista~Musica nos registros que A outra forma de fazer isso com $ echo $j
esto em $Str e lista cada artista que uma sintaxe muito semelhante ao for da $ let j++
toca aquela msica. Vamos executar o linguagem C, como vemos a seguir: $ echo $j
programa para ver se funciona mesmo: 1
for ((var=ini; cond; incr))
$ listamusica musica3 Musica4 U do Ou seja, a varivel $j sequer existia e
Eginha Pocot cmd1 no primeiro let assumiu o valor 0 (zero)
musica3 cmd2 para, aps o incremento, ter o valor 1.
Artista3 cmdn Veja s como as coisas cam simples:
Artista1 done
Musica4 for arq in *
Artista4 Onde var=ini signica que a vari- do
Eginha Pocot vel var comear de um valor inicial let i++
No encontrada ini; cond signica que o loop ou lao for echo $i -> $Arq
ser executado enquanto var no atingir done
A listagem cou feinha porque ainda a condio cond e incr signica o incre- 1 -> ArqDoDOS.txt1
no sabemos formatar a sada; mas mento que a varivel var sofrer a cada 2 -> confuso
qualquer dia desses, quando voc sou- passada do loop. Vamos aos exemplos: 3 -> incusu
ber posicionar o cursor, trabalhar com 4 -> listamusica
cores etc., faremos esse programa nova- for ((i=1; i<=9; i++)) 5 -> listartista
mente usando todas essas perfumarias. do 6 -> logado
A esta altura dos acontecimentos, echo -n $i 7 -> musexc
voc deve estar se perguntando: E done 8 -> musicas
aquele for tradicional das outras lingua- 1 2 3 4 5 6 7 8 9 9 -> musinc
gens em que ele sai contando a partir 10 -> muslist
de um nmero, com um determinado A varivel i partiu do valor inicial 1, o 11 -> testefor1
incremento, at alcanar uma condi- bloco de cdigo (aqui somente o echo) 12 -> testefor2
o?. E a que eu te respondo: Eu ser executado enquanto i for menor ou
no te disse que o nosso for mais por- igual (<=) a 9 e o incremento de i ser Pois amigo, tenho certeza que voc
reta que o dos outros? Para fazer isso, de 1 a cada passada do loop. j tomou um xarope do comando
existem duas formas. Com a primeira Repare que no for propriamente dito for. Por hoje chega, na prxima vez
sintaxe que vimos, como no exemplo: (e no no bloco de cdigo) no coloquei em que nos encontrarmos falaremos
um cifro ($) antes do i e a notao sobre outras instrues de loop, mas
for i in $(seq 9) para incrementar (i++) diferente do eu gostaria que at l voc zesse um
do que vimos at agora. O uso de parnte- pequeno script para contar a quanti-
echo -n $i ses duplos (assim como o comando let) dade de palavras de um arquivo texto,
done chama o interpretador aritmtico do cujo nome seria recebido como par-
1 2 3 4 5 6 7 8 9 Shell, que mais tolerante. metro. Essa contagem tem que ser
S para mostrar como o let funciona feita com o comando for, para se habi-
A varivel i assumiu os valores intei- e a versatilidade do for, vamos fazer a tuar ao seu uso. No vale usar o wc -w.
ros entre 1 a 9 gerados pelo comando mesma coisa, mas omitindo a ltima A Chico! Traz a saideira!
seq e a opo -n do echo foi usada para parte do escopo do for, passando-a para
no saltar uma linha a cada nmero lis- o bloco de cdigo: Julio Cezar Neves Analista de
SOBRE O AUTOR
tado. Ainda usando o for com seq: Suporte de Sistemas desde 1969 e tra-
for ((; i<=9;)) balha com Unix desde 1980, quando
for i in $(seq 4 9) do participou do desenvolvimento do
do let i++ SOX, um sistema operacional similar
echo -n $i echo -n $i ao Unix produzido pela Cobra Com-
putadores. Pode ser contatado no
done done
e-mail julio.neves@gmail.com
4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
92 edio 05 www.linuxmagazine.com.br
Papo de
Botequim VI
Blocos de cdigo e laos (ou loops, como preferem alguns)
Dave Hamilton - www.sxc.hu
86 edio 06 www.linuxmagazine.com.br
www.linuxmagazine.com.br edio 06 87
88 edio 06 www.linuxmagazine.com.br
Em 20/01 s 11:23h
Atalhos no
loop
www.linuxmagazine.com.br edio 06 89
Listagem 3: erreeme.sh
$ cat erreeme.sh then
#!/bin/bash echo "$Arq nao existe."
# Erro=3
# Salvando cpia de um arquivo antes de remov-lo continue # Volta para o comando for
# Tem de ter um ou mais arquivos a remover
if [ $# -eq 0 ] # Cmd. dirname informa nome do dir de $Arq
then DirOrig=`dirname $Arq`
echo "Erro -> Uso: erreeme arq [arq] ... [arq]" # Verica permisso de gravacao no diretrio
echo "O uso de metacaracteres e permitido. Ex.U if [ ! -w $DirOrig ]
erreeme arq*" then
exit 1 echo "Sem permisso no diretorio de $Arq"
Erro=4
continue # Volta para o comando for
# Varivel do sistema que contm o nome do usurio.
MeuDir="/tmp/$LOGNAME"
# Se no existir o meu diretrio sob o /tmp... # Se estou "esvaziando a lixeira"...
if [ ! -d $MeuDir ] if [ "$DirOrig" = "$MeuDir" ]
then then
mkdir $MeuDir # Vou cri-lo echo "$Arq cara sem copia de seguranca"
rm -i $Arq # Pergunta antes de remover
# Ser que o usurio removeu?
# Se no posso gravar no diretrio... [ -f $Arq ] || echo "$Arquivo removido"
if [ ! -w $MeuDir ] continue
then
echo "Impossivel salvar arquivos em $MeuDir. U
Mude as permisses..." # Guardo no m do arquivo o seu diretrio originalU
exit 2 para us-lo em um script de undelete
cd $DirOrig
pwd >> $Arq
# Varivel que indica o cod. de retorno do programa mv $Arq $MeuDir # Salvo e removo
Erro=0 echo "$Arq removido"
# Um for sem o in recebe os parametros passados done
for Arq
do # Passo eventual nmero do erro para o cdigo
# Se este arquivo no existir... # de retorno
if [ ! -f $Arq ] exit $Erro
um continue, para que a seqncia volte faa-o em casa e me traga para dis- um email para julio.neves@gmail.
para o loop do for de forma a receber cutirmos no nosso prximo encontro com. Agora chega de papo que eu j
outros arquivos. aqui no boteco. estou de goela seca de tanto falar. Me
Quando voc est no Windows (com Poxa, mas nesse eu acho que vou dan- acompanha no prximo chope ou j
perdo da m palavra) e tenta remover ar, pois no sei nem como comear... vai sair correndo para fazer o script
aquele monte de lixo com nomes esqui- Cara, este programa como tudo que passei?
sitos como HD04TG.TMP, se der erro o que se faz em Shell: extrema- Deixa eu pensar um pouco...
em um dos arquivos os outros no so mente fcil. para ser feito em, no Chico, traz mais um chope enquanto
removidos, no ? Ento, o continue foi mximo, 10 linhas. No se esquea ele pensa!
usado para evitar que uma improprie- de que o arquivo est salvo em /tmp/
dade dessas ocorra, isto , mesmo que $LOGNAME e que sua ltima linha Julio Cezar Neves Analista de
SOBRE O AUTOR
d erro na remoo de um arquivo, o o diretrio em que ele residia antes Suporte de Sistemas desde 1969 e tra-
programa continuar removendo os de ser removido. Tambm no se balha com Unix desde 1980, quando
outros que foram passados. esquea de criticar se foi passado o participou do desenvolvimento do
Eu acho que a esta altura voc deve nome do arquivo a ser removido. SOX, um sistema operacional similar
estar curioso para ver o programa eu vou tentar, mas sei no... ao Unix produzido pela Cobra Com-
putadores. Pode ser contatado no
que restaura o arquivo removido, no Tenha f, irmo, eu t te falando que
e-mail julio.neves@gmail.com
? Pois ento a vai vai um desao: mole! Qualquer dvida s passar
90 edio 06 www.linuxmagazine.com.br
Papo de Botequim
Curso de Shell Script
Parte VII
C
umequi, rapaz! Derreteu os pen- Pera, deixa eu ver se entendi o que voc , t vendo que o bichinho do shell te
samentos para fazer o scriptzinho fez: voc coloca na varivel Dir a ltima pegou. Vamos ver como ler dados, mas
que eu te pedi? linha do arquivo a ser restaurado, em antes vou te mostrar um comando que
, eu realmente tive de colocar muita nosso caso /tmp/$LOGNAME/$1 (onde te d todas as ferramentas para formatar
pensao na tela preta, mas acho que $LOGNAME o nome do usurio logado, uma tela de entrada de dados.
nalmente consegui! Bem, pelo menos e $1 o primeiro parmetro que voc
nos testes que z a coisa funcionou, passou ao script), j que foi l que arma- O comando tput
mas voc tem sempre que botar chifres zenamos o nome e caminho originais do O principal uso desse comando o posi-
em cabea de cachorro! arquivo antes de mov-lo para o diretrio cionamento do cursor na tela. Alguns
No bem assim. que programar em (de nido na varivel Dir). O comando parmetros podem no funcionar se o
Shell Script muito fcil, mas o que grep -v apaga essa linha, restaurando modelo de terminal de nido pela vari-
realmente importante so as dicas e o arquivo ao estado original, e o manda vel de ambiente $TERM no suport-los.
macetes que no so triviais. As cor- de volta pra onde ele veio. A ltima linha A tabela 1 apresenta apenas os principais
rees que fao so justamente para o apaga da lixeira. Sensacional! Impe- parmetros e os efeitos resultantes, mas
mostr-los. Mas vamos pedir dois cho- cvel! Nenhum erro! Viu? Voc j est existem muito mais deles. Para saber tudo
pes enquanto dou uma olhadela no teu pegando as manhas do shell! sobre o tput, veja a referncia [1].
script l na listagem 1. A Chico, traz Ento vamos l, chega de lesco-lesco Vamos fazer um programa bem besta
dois chopes! E no se esquea que um e bl-bl-bl, sobre o qu ns vamos e fcil para ilustrar melhor o uso desse
deles sem colarinho! falar hoje? comando. uma verso do famigerado
Al Mundo, s que dessa vez a frase
Listagem 1 restaura.sh ser escrita no centro da tela e em vdeo
reverso. Depois disso, o cursor voltar para
01 #!/bin/bash
a posio original. Veja a listagem 2.
02 #
Como o programa j est todo comen-
03 # Restaura arquivos deletados via erreeme
tado, acho que a nica linha que precisa de
04 #
explicao a 8, onde criamos a varivel
05
Coluna. O estranho ali aquele nmero
06 if [ $# -eq 0 ]
9, que na verdade indica o tamanho da
07 then
cadeia de caracteres que vou escrever na
08 echo "Uso: $0 <Nome do arquivo a ser restaurado>"
tela. Dessa forma, este programa somente
09 exit 1
conseguiria centralizar cadeias de 9 carac-
10 fi
teres, mas veja isto:
11 # Pega nome do arquivo/diretrio original na ltima linha
12 Dir='tail -1 /tmp/$LOGNAME/$1'
$ var=Papo
13 # O grep -v exclui a ltima linha e recria o arquivo com o diretrio
$ echo ${#var}
14 # e nome originalmente usados
4
15 grep -v $Dir /tmp/$LOGNAME/$1 > $Dir/$1
$ var="Papo de Botequim"
16 # Remove o arquivo que j estava moribundo
$ echo ${#var}
17 rm /tmp/$LOGNAME/$1
16
eu te apresentei quando falvamos do para que o \n fosse entendido como $ read -t2 -p "Digite seu nome completo: U
comando for, ser que ainda acredita uma quebra de linha (new line) e no " Nom || echo 'Eita moleza!'
nisso? Vamos testar: como um literal. Sob o Bash exis- Digite seu nome completo: Eita moleza!
tem diversas opes do read que ser- $ echo $Nom
$ oIFS="$IFS" vem para facilitar a sua vida. Veja
$ IFS=: a tabela 2. O exemplo acima foi uma brincadeira,
$ read var1 var2 var3 E agora direto aos exemplos curtos pois eu s tinha 2 segundos para digitar o
Papo de Botequim para demonstrar estas opes. Para ler meu nome completo e mal tive tempo de
$ echo $var1 um campo Matrcula: teclar um J (aquele colado no Eita), mas
Papo de Botequim ele serviu para mostrar duas coisas:
$ echo $var2 # -n no salta linha P 1) O comando aps o par de barras verti-
$ echo $var3 $ echo -n "Matricula: "; read Mat cais (o ou or lgico, lembra-se?) ser
$ read var1 var2 var3 Matricula: 12345 executado caso a digitao no tenha
Papo:de:Botequim $ echo $Mat sido concluda no tempo estipulado;
$ echo $var1 12345 P 2) A varivel Nom permaneceu vazia. Ela
Papo s receber um valor quando o ENTER
$ echo $var2 Podemos simplicar as coisas usando for teclado.
de a opo -p:
$ echo $var3 $ read -sp Senha:
Botequim $ read -p "Matricula: " Mat Senha: $ echo $REPLY
$ IFS="$oIFS" Matricula: 12345 segredo :)
$ echo $Mat
Viu? eu estava furado! O read l uma 12345 Aproveitei um erro no exemplo ante-
lista, assim como o for, separada pelos rior para mostrar um macete. Quando
caracteres da varivel $IFS. Veja como E podemos ler apenas uma quantidade escrevi a primeira linha, esqueci de colo-
isso pode facilitar a sua vida: pr-determinada de caracteres: car o nome da varivel que iria receber a
senha e s notei isso quando ia escrev-
$ grep julio /etc/passwd $ read -n5 -p"CEP: " Num ; read -n3 U la. Felizmente a varivel $REPLY do Bash
julio:x:500:544:Julio C. Neves - 7070:U -p- Compl contm a ltima seqncia de caracteres
/home/julio:/bin/bash CEP: 12345-678$ digitada e me aproveitei disso para no
$ oIFS="$IFS" # Salva o IFS antigo. $ echo $Num perder a viagem. Teste voc mesmo o que
$ IFS=: 12345 acabei de fazer.
$ grep julio /etc/passwd | read lname U $ echo $Compl O exemplo que dei, na verdade, era para
lixo uid gid coment home shell 678 mostrar que a opo -s impede que o que
$ echo -e "$lname\n$uid\n$gid\n$comentU est sendo digitado seja mostrado na tela.
\n$home\n$shell" No exemplo acima executamos duas Como no exemplo anterior, a falta do new
julio vezes o comando read: um para a pri- line fez com que o prompt de comando
500 meira parte do CEP e outra para o seu ($) permanecesse na mesma linha.
544 complemento, deste modo formatando Agora que sabemos ler da tela, vejamos
Julio C. Neves - 7070 a entrada de dados. O cifro ($) logo como se lem os dados dos arquivos.
/home/julio aps o ltimo algarismo digitado
/bin/bash necessrio porque o read no inclui Lendo arquivos
$ IFS="$oIFS" # Restaura o IFS por padro um caractere new line Como eu j havia lhe dito, e voc deve se
implcito, como o echo. lembrar, o while testa um comando e exe-
Como voc viu, a sada do grep foi Para ler s durante um determinado cuta um bloco de instrues enquanto esse
redirecionada para o comando read, limite de tempo (tambm conhecido comando for bem sucedido. Ora, quando
que leu todos os campos de uma s como time out): voc est lendo um arquivo para o qual
tacada. A opo -e do echo foi usada voc tem permisso de leitura, o read s
ser mal sucedido quando alcanar o EOF
Tabela 2: Opes do read (End Of File Fim do Arquivo). Portanto,
podemos ler um arquivo de duas maneiras.
Opo Ao A primeira redirecionando a entrada do
-p prompt Escreve prompt na tela antes de fazer a leitura arquivo para o bloco while, assim:
-n num L at num caracteres
while read Linha
-t seg Espera seg segundos para que a leitura seja concluda do
-s No exibe na tela os caracteres digitados. echo $Linha
done < arquivo
A segunda redirecionando a sada Como voc viu, o script lista suas pr- que vou mostrar com um exemplo prtico.
de um cat para o while, da seguinte prias linhas com um sinal de menos (-) Suponha que voc queira listar um arquivo
maneira: antes e outro depois de cada uma e, no e quer que a cada dez registros essa listagem
nal, exibe o contedo da varivel $Ultimo. pare para que o operador possa ler o con-
cat arquivo | Repare, no entanto, que o contedo dessa tedo da tela, e que ela s continue depois
while read Linha varivel permanece vazio. U, ser que de o operador pressionar qualquer tecla.
do a varivel no foi atualizada? Foi, e isso Para no gastar papel (da Linux Magazine),
echo $Linha pode ser comprovado porque a linha echo vou fazer essa listagem na horizontal. Meu
done "-$Ultimo-" lista corretamente as linhas. arquivo (numeros) tem 30 registros com
Ento por que isso aconteceu? nmeros seqenciais. Veja:
Cada um dos processos tem suas van- Como eu disse, o bloco de instrues
tagens e desvantagens. O primeiro mais redirecionado pelo pipe (|) executado $ seq 30 > numeros
rpido e no necessita de um subshell em um subshell e, l, as variveis so $ cat 10porpag.sh
para assisti-lo mas, em contrapartida, o atualizadas. Quando esse subshell ter- #!/bin/bash
redirecionamento ca pouco visvel em mina, as atualizaes das variveis vo # Programa de teste para escrever
um bloco de instrues grande, o que para as profundezas do inferno junto com # 10 linhas e parar para ler
por vezes prejudica a visualizao do ele. Repare que vou fazer uma pequena # Verso 1
cdigo. O segundo processo traz a van- mudana no script, passando o arquivo while read Num
tagem de que, como o nome do arquivo por redirecionamento de entrada (<), e do
est antes do while, a visualizao do as coisas passaro a funcionar na mais let ContLin++ # Contando...
cdigo mais fcil. Entretanto, o Pipe perfeita ordem: # -n para no saltar linha
(|) chama um subshell para interpret-lo, echo -n "$Num "
tornando o processo mais lento e pesado. $ cat redirread.sh ((ContLin % 10)) > /dev/null || read
Para ilustrar o que foi dito, veja os exem- #!/bin/bash done < numeros
plos a seguir: # redirread.sh
# Exemplo de read passando o arquivo Na tentativa de fazer um programa
$ cat readpipe.sh # por um pipe. genrico criamos a varivel $ContLin (na
#!/bin/bash Ultimo="(vazio)" vida real, os registros no so somente
# readpipe.sh # Passa o script ($0) para o while nmeros seqenciais) e, quando testamos
# Exemplo de read passando um arquivo while read Linha se o resto da diviso era zero, mandamos
# por um pipe. do a sada para /dev/null, pra que ela no
Ultimo="(vazio)" Ultimo="$Linha" aparea na tela. Mas quando fui executar
# Passa o script ($0) para o while echo "-$Ultimo-" o programa descobri a seguinte zebra:
cat $0 | while read Linha done < $0
do echo "Acabou, ltimo=:$Ultimo:" $ 10porpag.sh
Ultimo="$Linha" 1 2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 U
echo "-$Ultimo-" Veja como ele roda perfeitamente: 18 19 20 21 23 24 25 26 27 28 29 30
done
echo "Acabou, ltimo=:$Ultimo:" $ redirread.sh Repare que faltou o nmero 11 e a lista-
-#!/bin/bash- gem no parou no read. Toda a entrada do
Vamos ver o resultado de sua execuo: -# redirread.sh- loop estava redirecionada para o arquivo
-# Exemplo de read passando o arquivo numeros e a leitura foi feita em cima desse
$ readpipe.sh -# por um pipe.- arquivo, perdendo o nmero 11. Vamos
-#!/bin/bash- -- mostrar como deveria car o cdigo para
-# readpipe.sh- -Ultimo="(vazio)"- que ele passe a funcionar a contento:
-# Exemplo de read passando um arquivo -while read Linha-
-# por um pipe.- -do- $ cat 10porpag.sh
-- -Ultimo="$Linha"- #!/bin/bash
-Ultimo="(vazio)"- -echo "-$Ultimo-"- # Programa de teste para escrever
-# Passa o script ($0) para o while- -# Passa o script ($0) para o while- # 10 linhas e parar para ler - Verso 2
-cat $0 | - -done < $0- while read Num
-while read Linha- -echo "Acabou, ltimo=:$Ultimo:"- do
-do- Acabou, ltimo=:echo "Acabou,U let ContLin++ # Contando...
-Ultimo="$Linha"- ltimo=:$Ultimo:": # -n para no saltar linha
-echo "-$Ultimo-"- echo -n "$Num "
-done- Bem, amigos da Rede Shell, para nali- ((ContLin % 10)) > /dev/null || read U
-echo "Acabou, ltimo=:$Ultimo:"- zar a aula sobre o comando read s falta < /dev/tty
Acabou, ltimo=:(vazio): mais um pequeno e importante macete done < numeros
Repare que agora a entrada do read foi # 10 linhas e parar para ler Bem meu amigo, por hoje s porque
redirecionada para /dev/tty, que nada # Verso 3 acho que voc j est de saco cheio
mais seno o terminal corrente, expli- clear Num t no, pode continuar
citando desta forma que aquela leitura while read Num Se voc no estiver eu estou Mas j
seria feita do teclado e no do arquivo do que voc est to empolgado com o shell,
numeros. bom realar que isso no # Contando... vou te deixar um servio bastante sim-
acontece somente quando usamos o redi- ((ContLin++)) ples para voc melhorar a sua cdteca:
recionamento de entrada; se tivssemos echo "$Num" Monte toda a tela com um nico echo
usado o redirecionamento via pipe (|), o ((ContLin % (`tput lines` - 3))) || e depois posicione o cursor frente de
mesmo teria ocorrido. Veja agora a exe- { cada campo para receber o valor que
cuo do script: # para ler qualquer caractere ser digitado pelo operador.
read -n1 -p"Tecle Algo " < /dev/tty No se esqueam que, em caso de
$ 10porpag.sh # limpa a tela aps a leitura qualquer dvida ou falta de companhia
1 2 3 4 5 6 7 8 9 10 clear para um chope s mandar um e-mail
11 12 13 14 15 16 17 18 19 20 } para julio.neves@gmail.com. Vou aproveitar
21 22 23 24 25 26 27 28 29 30 done < numeros tambm para fazer uma propaganda:
digam aos amigos que quem estiver a
Isso est quase bom, mas ainda falta A mudana substancial feita neste exem- m de fazer um curso porreta de pro-
um pouco para car excelente. Vamos plo com relao quebra de pgina, j gramao em shell deve mandar um e-
melhorar o exemplo para que voc o que ela feita a cada quantidade-de-linhas- mail para julio.neves@tecnohall.com.br para
reproduza e teste (mas, antes de testar, da-tela (tput lines) menos (-) trs, isto informar-se. At mais!
aumente o nmero de registros em nume- , se a tela tem 25 linhas, o programa
ros ou reduza o tamanho da tela, para listar 22 registros e parar para leitura. Informaes
que haja quebra de linha). No comando read tambm foi feita uma [1] Pgina ocial do Tput: http://www.cs.utah.edu/
alterao, inserido o parmetro -n1 para dept/old/texinfo/tput/tput.html#SEC4
$ cat 10porpag.sh ler somente um caractere qualquer, no
[2] Pgina ocial do Bash: http://www.gnu.org/
#!/bin/bash necessariamente um ENTER, e a opo software/bash/bash.html
# Programa de teste para escrever -p para exibir uma mensagem.
Papo de Botequim
Curso de Shell Script
Parte VIII
E
a, cara, tudo bem? posicionais ($1, $2, , $n). Todas as regras que se aplicam
Tudo beleza! Eu queria te mostrar o que z mas j sei
que voc vai querer molhar o bico primeiro, n?
passagem de parmetros para programas tambm valem para
funes, mas muito importante realar que os parmetros
S pra te contrariar, hoje no quero. Vai, mostra logo a passados para um programa no se confundem com aqueles
o que voc fez. que so passados para suas funes. Isso signica, por exem-
Poxa, o exerccio que voc passou muito grande. D uma plo, que o $1 de um script diferente do $1 de uma de suas
olhada na listagem 1 e v como eu resolvi: funes internas.
, o programa t legal, t todo estruturadinho, mas gosta- Repare que as variveis $Msg, $TamMsg e $Col so de uso
ria de fazer alguns poucos comentrios: s para relembrar, restrito dessa rotina e, por isso, foram criadas como variveis
as seguintes construes: [ ! $Album ] && e [ $Musica ] locais. A razo simplesmente a economia de memria, j que
|| representam a mesma coisa, isto : no caso da primeira, ao sair da rotina elas sero devidamente detonadas, coisa que
testamos se a varivel $Album no (!) tem nada dentro, ento no aconteceria se eu no tivesse usado esse artifcio.
(&&) Na segunda, testamos se $Musica tem algum dado, A linha de cdigo que cria a varivel local Msg concatena ao
seno (||) texto recebido ($1) um parntese, a resposta padro ($2) em
Se voc reclamou do tamanho do programa, porque ainda caixa alta, uma barra, a outra resposta ($3) em caixa baixa
no te dei algumas dicas. Repare que a maior parte do script e naliza fechando o parntese. Uso essa conveno para, ao
para mostrar mensagens centraliza-
das na penltima linha da tela. Repare Listagem 1: musinc5.sh
ainda que algumas mensagens pedem
01 $ cat musinc5.sh
um S ou um N como resposta e outras
02 #!/bin/bash
so s de advertncia. Isso um caso
03 # Cadastra CDs (versao 5)
tpico que pede o uso de funes, que
04 #
seriam escritas somente uma vez e
05 clear
executadas em diversos pontos do
06 LinhaMesg=$((`tput lines` - 3)) # Linha onde sero mostradas as msgs para o operador
script. Vou montar duas funes para
07 TotCols=$(tput cols) # Qtd colunas da tela para enquadrar msgs
resolver esses casos e vamos incor-
08 echo
por-las ao seu programa para ver o
Incluso de Msicas
resultado nal.
======== == =======
Chico! Agora traz dois chopes, um
Ttulo do lbum:
sem colarinho, para me dar inspira-
| Este campo foi
o. E voc, de olho na listagem 2.
Faixa: < criado somente para
Como podemos ver, uma funo
| orientar o preenchimento
de nida quando digitamos nome_da_
Nome da Msica:
funo () e todo o seu corpo est
Intrprete: # Tela montada com um nico echo
entre chaves ({}). J conversamos aqui
09 while true
no boteco sobre passagem de par-
10 do
metros e as funes os recebem da
11 tput cup 5 38; tput el # Posiciona e limpa linha
mesma forma, isto , so parmetros
Conselho de amigo: crie um arquivo e anexe a ele cada fun- diretrio /home. S que assim que a execuo do script termi-
o nova que voc criar. Ao nal de algum tempo voc ter nou, o sub-shell foi para o belelu e, com ele, todo o ambiente
uma bela biblioteca de funes que lhe poupar muito tempo criado. Agora preste ateno no exemplo abaixo e veja como a
de programao. coisa muda de gura:
09 fi
Sistemas desde 1969 e trabalha com Unix
10 IFS=
11 : desde 1980, quando participou do desen-
12 for ArtMus in $(cut -f2 -d^ musicas) volvimento do SOX, um sistema operacio-
13 do nal similar ao Unix produzido pela Cobra
14 echo $ArtMus | grep -i ^$*~ > /dev/null && echo $ArtMus | cut -f2 -d~ Computadores. Pode ser contatado no
15 done e-mail julio.neves@gmail.com
Papo de Botequim
Curso de Shell Script
Parte IX
T
bom, j sei que voc vai querer chope antes de comear, Bom, a resposta "mais ou menos". Com estes comandos
mas t to a m de te mostrar o que z que j vou pedir a voc escreve 90% do que precisa, porm se precisar escrever
rodada enquanto isso. A Chico, manda dois! O dele sem algo formatado eles lhe daro muito trabalho. Para formatar a
colarinho pra no deixar cheiro ruim nesse bigodo sada veremos agora uma instruo muito mais interessante,
Enquanto o chope no chega, deixa eu te lembrar o que a printf. Sua sintaxe a seguinte:
voc me pediu na edio passada: era para refazer
o programa listartista com a tela formatada e exe- Listagem 1: mandamsg.func e pergunta.func
cuo em loop, de forma que ele s termine quando mandamsg.func
receber um [ENTER] sozinho como nome do artista. 01 # A funo recebe somente um parmetro
Eventuais mensagens de erro e perguntas feitas ao 02 # com a mensagem que se deseja exibir.
usurio deveriam ser mostradas na antepenltima 03 # Para no obrigar o programador a passar
linha da tela, utilizando para isso as rotinas externas 04 # a msg entre aspas, usaremos $* (todos
mandamsg.func e pergunta.func que desenvolvemos 05 # os parmetro, lembra?) e no $1.
06 Msg="$*"
durante nosso papo na edio passada.
07 TamMsg=${#Msg}
Primeiramente eu dei uma encolhida nas rotinas
08 Col=$(((TotCols - TamMsg) / 2)) # Centra msg na linha
mandamsg.func e pergunta.func, que caram como 09 tput cup $LinhaMesg $Col
na listagem 1. E na listagem 2 voc tem o grando, 10 read -n1 -p "$Msg "
nossa verso refeita do listaartista.
Puxa, voc chegou com a corda toda! Gostei da pergunta.func
forma como voc resolveu o problema e estruturou 01 # A funo recebe 3 parmetros na seguinte ordem:
02 # $1 - Mensagem a ser mostrada na tela
o programa. Foi mais trabalhoso, mas a apresenta-
03 # $2 - Valor a ser aceito com resposta padro
o cou muito legal e voc explorou bastante as
04 # $3 - O outro valor aceito
opes do comando tput. Vamos testar o resultado
05 # Supondo que $1=Aceita?, $2=s e $3=n, a linha
com um lbum do Emerson, Lake & Palmer que 06 # abaixo colocaria em Msg o valor "Aceita? (S/n)"
tenho cadastrado. 07 Msg="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)"
08 TamMsg=${#Msg}
Envenenando a escrita 09 Col=$(((TotCols - TamMsg) / 2)) # Centraliza msg na linha
Ufa! Agora voc j sabe tudo sobre leitura de dados, 10 tput cup $LinhaMesg $Col
mas quanto escrita ainda est apenas engatinhando. 11 read -n1 -p "$Msg " SN
12 [ ! $SN ] && SN=$2 # Se vazia coloca default em SN
J sei que voc vai me perguntar: Ora, no com o
13 SN=$(echo $SN | tr A-Z a-z) # A sada de SN ser em minscula
comando echo e com os redirecionamentos de sada
14 tput cup $LinhaMesg $Col; tput el # Apaga msg da tela
que se escreve dados?.
Listagem 2: listaartista
printf formato [argumento...] $ cat listartista3.sh
01 #!/bin/bash
Onde formato uma cadeia 02 # Dado um artista, mostra as suas musicas
03 # versao 3
de caracteres que contm trs
04 LinhaMesg=$((`tput lines` - 3)) # Linha onde as msgs sero mostradas
tipos de objeto: caracteres 05 TotCols=$(tput cols) # Qtd de colunas na tela para enquadrar msgs
simples, caracteres para es- 06 clear
pecicao de formato (ou de 07 echo "
+-----------------------------------+
controle) e seqncia de esca-
| Lista Todas as Msicas de um Determinado Artista |
pe no padro da linguagem | ===== ===== == ======= == == =========== ======= |
C. argumento a cadeia de | |
caracteres a ser impressa sob | Informe o Artista: |
+-----------------------------------+"
o controle de formato.
08 while true
Cada um dos caracteres uti- 09 do
lizados precedido pelo ca- 10 tput cup 5 51; tput ech 31 # ech=Erase chars (31 para no apagar barra vertical)
racter % e, logo a seguir, vem 11 read Nome
a especicao de formato de 12 if [ ! "$Nome" ] # $Nome esta vazio?
13 then
acordo com a tabela 1.
14 . pergunta.func "Deseja Sair?" s n
As seqncias de escape 15 [ $SN = n ] && continue
padro da linguagem C so 16 break
sempre precedidas pelo ca- 17 fi
18 fgrep -iq "^$Nome~" musicas || # fgrep no interpreta ^ como expresso regular
ractere contra-barra (\). As
19 {
reconhecidas pelo comando 20 . mandamsg.func "No existe msica desse artista"
printf so as da tabela 2. 21 continue
No acaba por a no! Tem 22 }
23 tput cup 7 29; echo '| |'
muito mais coisa sobre essa
24 LinAtual=8
instruo, mas como esse 25 IFS="
um assunto muito cheio de 26 :"
detalhes e, portanto, chato 27 for ArtMus in $(cut -f2 -d^ musicas) # Exclui nome do album
28 do
para explicar e pior ainda para
29 if echo "$ArtMus" | grep -iq "^$Nome~"
ler ou estudar, vamos passar 30 then
direto aos exemplos com co- 31 tput cup $LinAtual 29
mentrios. Veja s: 32 echo -n '| "
33 echo $ArtMus | cut -f2 -d~
34 tput cup $LinAtual 82
$ printf "%c" "1 caracter"
35 echo '|'
1$ 36 let LinAtual++
37 if [ $LinAtual -eq $LinhaMesg ]
Errado! S listou 1 caractere 38 then
39 . mandamsg.func "Tecle Algo para Continuar..."
e no saltou linha ao nal
40 tput cup 7 0; tput ed # Apaga a tela a partir da linha 7
41 tput cup 7 29; echo '| |'
$ printf "%c\n" "1 caracter" 42 LinAtual=8
1 43 fi
44 fi
45 done
Saltou linha mas ainda no 46 tput cup $LinAtual 29; echo '| |'
listou a cadeia inteira 47 tput cup $((++LinAtual)) 29
48 read -n1 -p "+--Tecle Algo para Nova Consulta+"
49 tput cup 7 0; tput ed # Apaga a tela a partir da linha 7
$ printf "%c caractere\n" 1
50 done
1 caractere
Tabela 1: Formatos de caractere (%) signica o tamanho que a cadeia ter O bc devolveu duas casas decimais e
aps a execuo do comando. Vamos ver o printf colocou o zero direita. O co-
Caractere A expresso ser impressa como:
c Caractere simples a seguir mais alguns exemplos. Os co- mando a seguir:
d Nmero no sistema decimal mandos abaixo:
e Notao cientca exponencial $ printf "%o\n" 10
f Nmero com ponto decimal (oat) $ printf "%d\n" 32 12
g O menor entre os formatos %e e %f com 32
omisso dos zeros no signicativos $ printf "%10d\n" 32 Converteu o valor 10 para base octal.
o Nmero no sistema octal 32 Para melhorar experimente:
s Cadeia de caracteres
x Nmero no sistema hexadecimal
preenchem a string com espaos em $ printf "%03o\n" 27
% Imprime um %. No h nenhum
tipo de converso branco esquerda (oito espaos mais dois 033
caracteres, 10 dgitos), no com zeros. J
no comando abaixo: Assim a converso ca com mais jeito
Opa, essa a forma correta! O %c rece- de octal, n?. O que este aqui faz?
beu o valor 1, como queramos: $ printf "%04d\n" 32
0032 $ printf "%s\n" Peteleca
$ a=2 Peteleca
$ printf "%c caracteres\n" $a O 04 aps % signica formate a string $ printf "%15s\n" Peteleca
2 caracteres em quatro dgitos, com zeros esquerda Peteleca
se necessrio. No comando:
O %c recebeu o valor da varivel $a. Imprime Peteleca com 15 caracteres.
$ printf "%e\n" $(echo "scale=2 ; 100/6" | bc) A cadeia de caracteres preenchida com
$ printf "%10c caracteres\n" $a 1.666000e+01 espaos em branco esquerda. J no co-
2 caracteres mando:
$ printf "%10c\n" $a caracteres O padro do %e seis casas decimais.
2 J no comando: $ printf "%-15sNeves\n" Peteleca
c Peteleca Neves
$ printf "%.2e\n" `echo "scale=2 ; 100/6" | bc`
Repare que, nos dois ltimos exemplos, 1.67e+01 O menos (-) colocou espaos em branco
em virtude do uso do %c, s foi listado direita de Peteleca at completar os 15
um caractere de cada cadeia de caracteres O .2 especicou duas casas decimais. caracteres pedidos. E o comando abaixo,
passada como parmetro. O valor 10 Observe agora: o que faz?
frente do c no signica 10 caracteres. Um
nmero seguindo o sinal de percentagem $ printf "%f\n" 32.3 $ printf "%.3s\n" Peteleca
32.300000 Pet
Tabela 2: Seqncias de escape
O padro do %f seis casas decimais. O .3 manda truncar a cadeia de ca-
Seqncia Efeito
E no comando: racteres aps as trs primeiras letras. E
a Soa o beep
b Volta uma posio (backspace) o comando a seguir:
f Salta para a prxima pgina $ printf "%.2f\n" 32.3
lgica ( form feed) 32.30 $ printf "%10.3sa\n" Peteleca
n Salta para o incio da linha se- Peta Pet
guinte (line feed) O .2 especicou duas casas decimais.
r Volta para o incio da linha cor-
Agora observe: Imprime a cadeia com 10 caracteres,
rente (carriage return)
truncada aps os trs primeiros, conca-
t Avana para a prxima marca de
tabulao $ printf "%.3f\n" `echo "scale=2 ; 100/6" | bc` tenada com o caractere a (aps o s). E
33.330 esse comando a seguir, o que faz?
$ printf EXEMPLO %x\n 45232 trabalho, principalmente em instalaes (1 minuto). A cada intervalo o Shell far
EXEMPLO b0b0 com estrutura de diretrios em mltiplos a vericao antes de exibir o prximo
nveis. Veja o exemplo a seguir: prompt primrio ($PS1). Se essa varivel
Ele transformou o nmero 45232 para estiver sem valor ou com um valor menor
hexadecimal (b0b0), mas os zeros no $ echo $CDPATH ou igual a zero, a busca por novas men-
combinam com o resto. Experimente: .:..:~:/usr/local sagens no ser efetuada.
$ pwd PATH Caminhos que sero pesquisa-
$ printf EXEMPLO %X\n 45232 /home/jneves/LM dos para tentar localizar um arquivo espe-
EXEMPLO B0B0 $ cd bin cicado. Como cada script um arquivo,
$ pwd caso use o diretrio corrente (.) na sua
Assim disfarou melhor! (repare no X /usr/local/bin varivel $PATH, voc no necessitar usar
maisculo). Pra terminar, que tal o co- o comando ./scrp para que o script scrp
mando abaixo: Como /usr/local estava na minha seja executado. Basta digitar scrp. Este
varivel $CDPATH e no existia o diretrio o modo que prero.
$ printf %X %XL%X\n 49354 192 10 bin em nenhum dos seus antecessores (., PIPESTATUS uma varivel do tipo
C0CA C0LA .. e ~), o comando cd foi executado tendo vetor (array) que contm uma lista de
como destino /usr/local/bin. valores de cdigos de retorno do ltimo
Este a no marketing e bastante HISTSIZE Limita o nmero de ins- pipeline executado, isto , um array que
completo, veja s como funciona: trues que cabem dentro do arquivo de abriga cada um dos $? de cada instruo
O primeiro %X converteu 49354 em he- histrico de comandos (normalmente do ltimo pipeline. Para entender melhor,
xadecimal, resultando em C0CA (leia-se .bash_history, mas na verdade o que veja o exemplo a seguir:
c, zero, c e a). Em seguida veio est indicado na varivel $HISTFILE). Seu
um espao em branco seguido por outro valor padro 500. $ who
%XL. O %X converteu o 192 dando como HOSTNAME O nome do host corrente jneves pts/0 Apr 11 16:26 (10.2.4.144)
resultado C0 que com o L fez C0L. E nal- (que tambm pode ser obtido com o co- jneves pts/1 Apr 12 12:04 (10.2.4.144)
mente o ltimo parmetro %X transformou mando uname -n). $ who | grep ^botelho
o nmero 10 na letra A. LANG Usada para determinar o idioma $ echo ${PIPESTATUS[*]}
Conforme vocs podem notar, a instru- falado no pas (mais especicamente ca- 0 1
o bastante completa e complexa. Ain- tegoria do locale). Veja um exemplo:
da bem que o echo resolve quase tudo... Neste exemplo mostramos que o usu-
Acertei em cheio quando resolvi expli- $ date rio botelho no estava logado, em
car o printf atravs de exemplos, pois Thu Apr 14 11:54:13 BRT 2005 seguida executamos um pipeline que
no saberia como enumerar tantas regri- $ LANG=pt_BR date procurava por ele. Usa-se a notao [*]
nhas sem tornar a leitura enfadonha. Qui Abr 14 11:55:14 BRT 2005 em um array para listar todos os seus
elementos; dessa forma, vimos que a pri-
Principais variveis do Shell LINENO O nmero da linha do script meira instruo (who) foi bem-sucedida
O Bash possui diversas variveis que ou funo que est sendo executada. (cdigo de retorno 0) e a seguinte (grep)
servem para dar informaes sobre Seu uso principal mostrar mensagens no (cdigo de retorno 1).
o ambiente ou alter-lo. So muitas e de erro juntamente com as variveis $0 PROMPT_COMMAND Se esta varivel re-
no pretendo mostrar todas elas, mas (nome do programa) e $FUNCNAME (nome ceber o nome de um comando, toda vez
uma pequena parte pode lhe ajudar na da funo em execuo). que voc teclar um [ENTER] sozinho no
elaborao de scripts. Veja a seguir as LOGNAME Esta varivel armazena o prompt principal ($PS1), esse comando
principais delas: nome de login do usurio . ser executado. muito til quando voc
CDPATH Contm os caminhos que MAILCHECK Especica, em segundos, a precisa repetindo constantemente uma
sero pesquisados para tentar localizar freqncia com que o Shell verica a pre- determinada instruo.
um diretrio especicado. Apesar dessa sena de correspondncia nos arquivos PS1 o prompt principal. No Papo
varivel ser pouco conhecida, seu uso indicados pela variveis $MAILPATH ou de Botequim usamos os padres $ para
deve ser incentivado por poupar muito $MAIL. O tempo padro de 60 segundos usurio comum e # para root, mas mui-
${cadeia/%subcad1/subcad2} Se subcad1 combina com o m de cadeia, ento trocado por subcad2 ${Passaro/%quero/no}
Como diz o nordestino - quero no
$ echo $cadeia $ echo ${cadeia/*po/Conversa} Agora j chega, o papo hoje foi chato
Papo de Botequim Conversa de Botequim porque teve muita decoreba, mas o que
$ echo ${cadeia%' '*} $ echo ${cadeia/????/Conversa} mais importa voc ter entendido o
Papo de Conversa de Botequim que te falei. Quando precisar, consulte
$ echo ${cadeia%%' '*} estes guardanapos onde rabisquei as
Papo Trocando todas as ocorrncias de uma dicas e depois guarde-os para consultas
subcadeia por outra. O comando: futuras. Mas voltando vaca fria: t
Para trocar primeira ocorrncia de uma na hora de tomar outro e ver o jogo do
subcadeia em uma cadeia por outra: $ echo ${cadeia//o/a} Mengo. Pra prxima vez vou te dar
Papa de Batequim moleza e s vou cobrar o seguinte: pe-
$ echo $cadeia gue a rotina pergunta.func (da qual
Papo de Botequim Ordena a troca de todos as letras o por falamos no incio do nosso bate-papo
$ echo ${cadeia/de/no} a. Outro exemplo mais til para contar de hoje, veja a listagem 1) e otimize-a
Papo no Botequim a quantidade de arquivos existentes no para que a varivel $SN receba o valor
$ echo ${cadeia/de /} diretrio corrente. Observe o exemplo: padro por expanso de parmetros,
Papo Botequim como vimos.
$ ls | wc -l E no se esquea: em caso de dvidas
Preste ateno quando for usar metaca- 30 ou falta de companhia para um (ou mais)
racteres! Eles so gulosos e sempre com- chope s mandar um e-mail para julio.
binaro com a maior possibilidade; No O wc pe um monte de espaos em neves@gmail.com. E diga para os amigos
exemplo a seguir eu queria trocar Papo de branco antes do resultado. Para tir-los: que quem estiver a m de fazer um curso
Botequim por Conversa de Botequim: porreta de programao em Shell deve
# QtdArqs recebe a sada do comando mandar um e-mail para julio.neves@tecnohall.
$ echo $cadeia $ QtdArqs=$(ls | wc -l) com.br para informar-se. Valeu!
Papo de Botequim $ echo ${QtdArqs/ * /}
$ echo ${cadeia/*o/Conversa} 30 Julio Cezar Neves Analista de Suporte de
Sobre o autor
Papo de Botequim
Curso de Shell Script
Parte X
E
a amigo, te dei a maior moleza na a legibilidade do cdigo est horrorvel, O comando eval
ltima aula n? Um exerciciozinho mas o desempenho, isto , a velocidade Vou te dar um problema que eu duvido
muito simples de execuo, est timo. Como funes que voc resolva:
, mas nos testes que eu z, e de so coisas muito pessoais, j que cada
acordo com o que voc ensinou sobre um usa as suas e quase no h neces- $ var1=3
substituio de parmetros, achei que sidade de manuteno, eu sempre opto $ var2=var1
deveria fazer algumas alteraes nas fun- pelo desempenho.
es que desenvolvemos para torn-las de Hoje vamos sair daquela chatura que Te dei essas duas variveis e quero que
uso geral, como voc disse que todas as foi o nosso ltimo papo e voltar lgica, voc me diga como eu posso, me referindo
funes deveriam ser. Quer ver? saindo da decoreba. Mas volto a te lem- apenas varivel a var2, listar o valor de
Claro, n, man, se te pedi para fazer brar: tudo que eu te mostrei da ltima vez var1 (que, no nosso caso, 3).
porque estou a m de te ver aprender, aqui no Boteco do Chico vlido e quebra Ah, isso mole, mole! s digitar
mas pera, d um tempo. Chico! Manda um galho. Guarde aqueles guardanapos esse comando aqui:
dois, um sem colarinho! Vai, mostra a que rabiscamos porque, mais cedo ou
o que voc fez. mais tarde, eles lhe vo ser muito teis. echo $`echo $var2`
Bem, alm do que voc pediu, eu
reparei que o programa que chamava a Listagem 1: funo pergunta.func
funo teria de ter previamente de nidas 01 # A funo recebe 3 parmetros na seguinte ordem:
a linha em que seria mostrada a mensa- 02 # $1 - Mensagem a ser mostrada na tela
gem e a quantidade de colunas. O que 03 # $2 - Valor a ser aceito com resposta padro
z foi incluir duas linhas nas quais 04 # $3 - O outro valor aceito
empreguei substituio de parmetros 05 # Supondo que $1=Aceita?, $2=s e $3=n, a linha
para que, caso uma dessas variveis no 06 # abaixo colocaria em Msg o valor "Aceita? (S/n)"
07 TotCols=${TotCols:-$(tput cols)} # Se no estava definido, agora est
fosse informada, ela recebesse um valor
08 LinhaMesg=${LinhaMesg:-$(($(tput lines)-3))} # Idem
atribudo pela prpria funo. A linha
09 Msg="$1 (`echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`)"
de mensagem trs linhas antes do m 10 TamMsg=${#Msg}
da tela e o total de colunas obtido pelo 11 Col=$(((TotCols - TamMsg) / 2)) # Para centralizar Msg na linha
comando tput cols. D uma olhada na 12 tput cup $LinhaMesg $Col
listagem 1 e veja como cou: 13 read -n1 -p "$Msg " SN
Gostei, voc j se antecipou ao que eu 14 SN=${SN:-$2} # Se vazia coloca o padro em SN
ia pedir. S pra gente encerrar esse papo 15 SN=$(echo $SN | tr A-Z a-z) # A sada de SN ser em minsculas
16 tput cup $LinhaMesg $Col; tput el # Apaga Msg da tela
de substituio de parmetros, repare que
Repare que eu coloquei o echo $var2 entre crases (`), porque $ var2=ls
dessa forma ele ter prioridade de execuo e resultar em var1. $ $var2
E echo $var1 produzir 3 10porpag1.sh alo2.sh incusu logado
Ah, ? Ento execute para ver se est correto. 10porpag2.sh ArqDoDOS.txt1 listamusica logaute.sh
10porpag3.sh confuso listartista mandamsg.func
$ echo $`echo $var2` alo1.sh contpal.sh listartista3 monbg.sh
$var1
Agora vamos colocar em var2 o seguinte: ls $var1; e em
U! Que foi que aconteceu? O meu raciocnio me parecia var1 vamos colocar l*, vejamos o resultado:
bastante lgico
O seu raciocnio realmente foi lgico, o problema que voc $ var2='ls $var1'
esqueceu de uma das primeiras coisas de que te falei aqui no $ var1='l*'
Boteco e que vou repetir. O Shell usa a seguinte ordem para $ $var2
resolver uma linha de comando: ls: $var1: No such file or directory
P Resolve os redirecionamentos; $ eval $var2
P Substitui as variveis pelos seus valores; listamusica listartista listartista3 logado logaute.sh
P Resolve e substitui os meta caracteres;
P Passa a linha j toda esmiuada para execuo. Novamente, no tempo de substituio das variveis, $var1
Dessa forma, quando o interpretador chegou na fase de re- ainda no havia se apresentado ao Shell para ser resolvida.
soluo de variveis, que como eu disse anterior execuo, Assim, s nos resta executar o comando eval para dar as duas
a nica varivel existente era var2 e por isso a tua soluo passadas necessrias.
produziu como sada $var1. O comando echo identicou isso Uma vez um colega da excelente lista de discusso groups.yahoo.com/
como uma cadeia de caracteres e no como uma varivel. group/shell-script colocou uma dvida: queria fazer um menu
Problemas desse tipo so relativamente freqentes e seriam que numerasse e listasse todos os arquivos com extenso .sh e,
insolveis caso no existisse a instruo eval, cuja sintaxe quando o operador escolhesse uma opo, o programa corres-
eval cmd, onde cmd uma linha de comando qualquer, que pondente fosse executado. Veja minha proposta na listagem 2:
voc poderia inclusive executar direto no prompt do terminal.
Quando voc pe o eval na frente, no entanto, o que ocorre Listagem 2: fazmenu.sh
que o Shell trata cmd como um parmetro do eval e, em seguida, 01 #!/bin/bash
o eval executa a linha recebida, submetendo-a ao Shell. Ou 02 #
seja, na prtica cmd analisado duas vezes. Dessa forma, se 03 # Lista que enumera os programas com extenso .sh no
executssemos o comando que voc props colocando o eval 04 # diretrio corrente e executa o escolhido pelo operador
05 #
na frente, teramos a sada esperada. Veja:
06 clear; i=1
07 printf "%11s\t%s\n\n" Opo Programa
$ eval echo $`echo $var2` 08 CASE='case $opt in'
3 09 for arq in *.sh
10 do
Esse exemplo tambm poderia ter sido feito de outra maneira. 11 printf "\t%03d\t%s\n" $i $arq
12 CASE="$CASE
D s uma olhada:
13 "$(printf "%03d)\t %s;;" $i $arq)
14 i=$((i+1))
$ eval echo \$$var2 15 done
3 16 CASE="$CASE
17 *) . erro;;
Na primeira passada a contrabarra (\) seria retirada e $var2 18 esac"
19 read -n3 -p "Informe a opo desejada: " opt
seria resolvido produzindo var1. Na segunda passada teria so-
20 echo
brado echo $var1, que produziria o resultado esperado. Agora 21 eval "$CASE"
vou colocar um comando dentro de var2 e executar:
Parece complicado porque usei muitos dos por) processos em execuo. Vamos, "limpar a rea" ao seu trmino. Se seu
printf para formatao da tela, mas na de agora em diante, dar uma olhadinha encerramento ocorrer de forma prevista,
verdade bastante simples: o primei- nos sinais enviados aos processos e mais ou seja, se tiver um trmino normal,
ro printf foi colocado para imprimir frente vamos dar uma passada rpida muito fcil fazer essa limpeza; porm,
o cabealho e logo em seguida come- pelos sinais gerados pelos processos. se o seu programa tiver um m brusco,
cei a montar dinamicamente a varivel Para mandar um sinal a um processo, muita coisa ruim pode ocorrer:
$CASE, na qual ao nal ser feito um eval usamos normalmente o comando kill, P possvel que em um determinado es-
para execuo do programa escolhido. cuja sintaxe : pao de tempo, o seu computador esteja
Repare no entanto que dentro do loop cheio de arquivos de trabalho inteis
do for existem dois printf: o primeiro $ kill -sig PID P Seu processador poder car atolado
serve para formatar a tela e o segundo de processos zombies e defuncts gera-
para montar o case (se antes do coman- Onde PID o identicador do proces- dos por processos lhos que perderam
do read voc colocar uma linha echo so (Process Identication ou Process ID). os pais e esto rfos;
"$CASE", ver que o comando case mon- Alm do comando kill, algumas seqn- P necessrio liberar sockets abertos para
tado dentro da varivel est todo inden- cias de teclas tambm podem gerar sinais. no deixar os clientes congelados;
tado. Frescura, n?:). Na sada do for, foi A tabela 1 mostra os sinais mais impor- P Seus bancos de dados podero car
adicionada uma linha varivel $CASE tantes para monitorarmos: corrompidos porque sistemas gerencia-
para, no caso de uma escolha invlida, Alm desses, existe o famigerado si- dores de bancos de dados necessitam
ser executada uma funo externa para nal -9 ou SIGKILL que, para o processo de um tempo para gravar seus buffers
exibir mensagens de erro. Vamos execu- que o est recebendo, equivale a meter em disco (commit).
tar o script para ver a sada gerada: o dedo no boto de desligar do compu- Enm, existem mil razes para no usar
tador o que altamente indesejvel, um kill com o sinal -9 e para monitorar o
$ fazmenu.sh j que muitos programas necessitam encerramento anormal de programas.
Opcao Programa
Listagem 3: Nova verso do fazmenu.sh
001 10porpag1.sh
01 #!/bin/bash
002 10porpag2.sh
02 #
003 10porpag3.sh 03 # Lista enumerando os programas com extenso .sh no
004 alo1.sh 04 # diretrio corrente; executa o escolhido pelo operador
005 alo2.sh 05 #
006 contpal.sh 06 clear; i=1
007 fazmenu.sh 07 printf "%11s\t%s\n\n" Opo Programa
008 logaute.sh 08 CASE='case $opt in'
009 monbg.sh 09 for arq in *.sh
010 readpipe.sh 10 do
011 redirread.sh 11 printf "\t%03d\t%s\n" $i $arq
Informe a opo desejada: 12 CASE="$CASE
13 "$(printf "%03d)\t %s;;" $i $arq)
Seria interessante incluir uma opo 14 i=$((i+1))
para terminar o programa e, para isso, 15 done
seria necessria a incluso de uma linha 16 printf "\t%d\t%s\n\n" 999 "Fim do programa" # Linha includa
aps o loop de montagem da tela e a alte- 17 CASE="$CASE
Se voc ignorar um sinal, todos os sub- Para terminar esse assunto, abra um cadeiadeopcoes deve ser abc. Se voc
shells iro ignor-lo. Portanto, se voc console grco e escreva no prompt de desejar que uma opo seja seguida por
especicar qual ao deve ser tomada comando o seguinte: um argumento, ponha um sinal de dois
quando receber um sinal, todos os sub- pontos (:) depois da letra, como em a:bc.
shells iro tomar a mesma ao quando $ trap "echo Mudou o tamanho da janela" 28 Isso diz ao getopts que a opo -a tem a
receberem esse sinal. Ou seja, os sinais forma -a argumento. Normalmente um
so automaticamente exportados. Para o Em seguida, pegue o mouse e arraste-o ou mais espaos em branco separam o
sinal mostrado (sinal 2), isso signica que de forma a variar o tamanho da janela parmetro da opo; no entanto, getopts
os sub-shells sero encerrados. Suponha corrente. Surpreso? o Shell orientado tambm manipula parmetros que vm
que voc execute o comando trap "" 2 e a eventos Mais unzinho, porque no colados opo como em -aargumento.
ento execute um sub-shell, que tornar a consigo resistir. Escreva isto: cadeiadeopcoes no pode conter um si-
executar outro script como um sub-shell. nal de interrogao (?).
Se for gerado um sinal de interrupo, $ trap "echo j era" 17 O nome constante da linha de sintaxe
este no ter efeito nem sobre o Shell acima dene uma varivel que receber,
principal nem sobre os sub-shell por ele Em seguida digite: a cada vez que o comando getopts for
chamados, j que todos eles ignoraro executado, o prximo dos parmetros
o sinal. $ sleep 3 & posicionais e o colocar na varivel nome.
Em korn shell (ksh) no existe a opo getopts coloca uma interrogao (?) na
-s do comando read para ler uma senha. Voc acabou de criar um sub-shell que varivel denida em nome se achar uma
O que costumamos fazer usar usar o ir dormir durante trs segundos em opo no denida em cadeiadeopcoes
comando stty com a opo -echo, que background. Ao m desse tempo, voc ou se no achar o argumento esperado
inibe a escrita na tela at que se encon- receber a mensagem j era, porque o para uma determinada opo.
tre um stty echo para restaurar essa sinal 17 emitido a cada vez em que um Como j sabemos, cada opo passada
escrita. Ento, se estivssemos usando o sub-shell termina a sua execuo. Para por uma linha de comandos tem um n-
interpretador ksh, a leitura da senha teria devolver esses sinais ao seu comporta- dice numrico; assim, a primeira opo
que ser feita da seguinte forma: mento padro, digite: trap 17 28. estar contida em $1, a segunda em $2 e
Muito legal esse comando, n? Se voc assim por diante. Quando o getopts obtm
echo -n "Senha: " descobrir algum material bacana sobre uma opo, ele armazena o ndice do
stty -echo uso de sinais, por favor me informe por prximo parmetro a ser processado na
read Senha email, porque muito rara a literatura varivel OPTIND.
stty echo sobre o assunto. Quando uma opo tem um argumento
associado (indicado pelo : na cadeiade-
O problema com esse tipo de constru- Comando getopts opcoes), getopts armazena o argumento
o que, caso o operador no soubes- O comando getopts recupera as opes e na varivel OPTARG. Se uma opo no
se a senha, ele provavelmente teclaria seus argumentos de uma lista de par- possuir argumento ou se o argumento
[CTRL]+[C] ou um [CTRL]+[\] durante metros de acordo com a sintaxe POSIX.2, esperado no for encontrado, a varivel
a instruo read para descontinuar o pro- isto , letras (ou nmeros) aps um sinal OPTARG ser "apagada" (com unset). O co-
grama e, caso agisse dessa forma, o seu de menos (-) seguidas ou no de um mando encerra sua execuo quando:
terminal estaria sem echo. Para evitar que argumento; no caso de somente letras P Encontra um parmetro que no come-
isso acontea, o melhor a fazer : (ou nmeros), elas podem ser agrupa- a com um hfen (-).
das. Voc deve usar esse comando para P O parmetro especial -- indica o m
echo -n "Senha: " "fatiar" opes e argumentos passados das opes.
trap "stty echo para o seu script. P Quando encontra um erro (por exemplo,
exit" 2 3 A sintaxe getopts cadeiadeopcoes uma opo no reconhecida).
stty -echo nome. A cadeiadeopcoes deve explicitar O exemplo da listagem 4 meramente
read Senha uma cadeia de caracteres com todas as didtico, servindo para mostrar, em um
stty echo opes reconhecidas pelo script; assim, pequeno fragmento de cdigo, o uso ple-
trap 2 3 se ele reconhece as opes -a -b e c, no do comando.
Papo de botequim
Curso de Shell Script
E
a rapaz, tudo bom? No estou nem a para a aparncia, o que eu queria que
Beleza. Voc se lembra de que da ltima vez voc me voc exercitasse o que aprendemos. Me d a listagem 1 pra eu
pediu para fazer um programa que imprimisse dinami- ver o que voc fez.
camente, no centro da tela, a quantidade de linhas e colunas Perfeito! Que se dane a aparncia, depois vou te ensinar
de um terminal sempre que o tamanho da janela variasse? uns macetes para melhor-la. O que vale que o programa est
Pois , eu at que z, mas mesmo depois de quebrar muito a funcionando e est bem enxuto.
cabea a aparncia no cou igual. Pxa, e eu que perdi o maior tempo tentando descobrir
como aumentar a fonte
Listagem 1: tamtela.sh Deixe isso para l! Hoje vamos ver umas coisas
01 #!/bin/bash bastante interessantes e teis.
02 #
03 # Coloca no centro da tela, em video reverso, Dando nomes aos canos
04 # a quantidade de colunas e linhas Um outro tipo de pipe o named pipe, que tambm
05 # quando o tamanho da tela eh alterado. chamado de FIFO. FIFO um acrnimo de First In
06 # First Out que se refere propriedade em que a ordem
07 trap Muda 28 # 28 = sinal gerado pela mudanca no tamanho
dos bytes entrando no pipe a mesma que a da sada.
08 # da tela e Muda eh a funcao que fara isso.
O name em named pipe , na verdade, o nome de um
09
10 Bold=$(tput bold) # Modo de enfase arquivo. Os arquivos tipo named pipes so exibidos
11 Rev=$(tput rev) # Modo de video reverso pelo comando ls como qualquer outro, com poucas
12 Norm=$(tput sgr0) # Restaura a tela ao padrao default diferenas, veja:
13
14 Muda () $ ls -l pipe1
15 { prw-r-r-- 1 julio dipao 0 Jan 22 23:11 pipe1|
16 clear
17 Cols=$(tput cols)
o p na coluna mais esquerda indica que fifo1
18 Lins=$(tput lines)
19 tput cup $(($Lins / 2)) $(((Cols - 7) / 2)) # Centro da tela
um named pipe. O resto dos bits de controle de
20 echo $Bold$Rev$Cols X $Lins$Norm permisses, quem pode ler ou gravar o pipe, funcio-
21 } nam como um arquivo normal. Nos sistemas mais
22 modernos uma barra vertical (|), ou pipe, no m do
23 clear nome do arquivo outra dica e, nos sistemas LINUX,
24 read -n1 -p "Mude o tamanho da tela ou tecle algo para terminar " onde o ls pode exibir cores, o nome do arquivo
escrito em vermelho por padro.
Nos sistemas mais antigos, os named pipes so criados pelo Sincronizao de processos
utilitrio mknod, normalmente situado no diretrio /etc. Nos Suponha que voc dispare paralelamente dois programas (pro-
sistemas mais modernos, a mesma tarefa feita pelo mkfo, cessos), chamados programa1 e programa2, cujos diagramas de
que recebe um ou mais nomes como argumento e cria pipes blocos de suas rotinas so como mostrado na tabela 1. Os dois
com esses nomes. Por exemplo, para criar um named pipe com processos so disparados em paralelo e, no bloco 1 do programa1,
o nome pipe1, digite: as trs classicaes so disparadas da seguinte maneira:
Tabela 1
Programa1 Programa2
Bloco 1 Rotina de classicao de trs grandes arquivos Rotina de abertura e gerao de menus
Bloco 2 Acertos nais e encerramento Impresso dos dados classicados pelo programa 1
$ ls -l >(cat) #!/bin/bash
l-wx 1 jneves jneves 64 Aug 27 12:26 /dev/fd/63 -> pipe:[7050] LIST="" # Criada no shell principal
ls | while read FILE # Inicio do subshell
Realmente um named pipe. Voc deve estar pensando do
que isto uma maluquice de nerd, n? Ento suponha que voc LIST="$FILE $LIST" # Alterada dentro do subshell
tenha dois diretrios, chamados dir e dir.bkp, e deseja saber done # Fim do subshell
se os dois so iguais. Basta comparar o contedo dos diretrios echo $LIST
com o comando cmp:
No incio deste exemplo eu disse que ele era meramente
$ cmp <(cat dir/*) <(cat dir.bkp/*) || echo backup furado didtico porque existem formas melhores de fazer a mesma
tarefa. Veja s estas duas:
ou, melhor ainda:
$ ls | ln
$ cmp <(cat dir/*) <(cat dir.bkp/*) >/dev/null || echo backup furado
ou ento, usando a prpria substituio de processos:
Este um exemplo meramente didtico, mas so tantos os
comandos que produzem mais de uma linha de sada que ele $ cat -n <(ls)
serve como guia para outros. Eu quero gerar uma listagem dos
meus arquivos, numerando-os, e ao nal mostrar o total de Um ltimo exemplo: voc deseja comparar arq1 e arq2 usando
arquivos no diretrio corrente: o comando comm, mas esse comando necessita que os arquivos
estejam classicados. Ento a melhor forma de proceder :
while read arq
do $ comm <(sort arq1) <(sort arq2)
((i++)) # assim nao eh necessario inicializar i
echo "$i: $arq" Essa forma evita que voc faa as seguintes operaes:
done < <(ls)
echo "No diretorio corrente (`pwd`) existem $i arquivos" $ sort arq1 > /tmp/sort1
$ sort arq2 > /tmp/sort2
T legal, eu sei que existem outras formas de executar a $ comm /tmp/sort1 /tmp/sort2
mesma tarefa. Usando o comando while, a forma mais comum $ rm -f /tmp/sort1 /tmp/sort2
de resolver esse problema seria:
Pessoal, o nosso papo de botequim chegou ao m. Curti muito
ls | while read arq e recebi diversos elogios pelo trabalho desenvolvido ao longo
do de doze meses e, o melhor de tudo, z muitas amizades e tomei
((i++)) # assim nao eh necessario inicializar i muitos chopes de graa com os leitores que encontrei pelos
echo "$i: $arq" congressos e palestras que ando fazendo pelo nosso querido
done Brasil. Me despeo de todos mandando um grande abrao aos
echo "No diretorio corrente (`pwd`) existem $i arquivos" barbudos e beijos s meninas e agradecendo os mais de 100
emails que recebi, todos elogiosos e devidamente respondidos.
Ao executar o script, tudo parece estar bem, porm no coman- sade de todos ns: Tim, Tim.
do echo aps o done, voc ver que o valor de $i foi perdido. - Chico, fecha a minha conta porque vou pra casa!
Isso deve-se ao fato desta varivel estar sendo incrementada em
Julio Cezar Neves Analista de Suporte de Sistemas desde 1969 e
Sobre o autor