papo de botequim
DESCRIPTION
ÂTRANSCRIPT
O ambiente LinuxPara você entender o que é e como fun-ciona o Shell, primeiro vou te mostrarcomo funciona o ambiente em camadasdo Linux. Dê uma olhada no gráficomostrado na Figura 1.
Neste gráfico podemos ver que a ca-mada de hardware é a mais profunda e éformada pelos componentes físicos doseu computador. Em torno dela, vem acamada do kernel que é o cerne doLinux, seu núcleo, e é quem põe o hard-ware para funcionar, fazendo seu geren-ciamento e controle. Os programas ecomandos que envolvem o kernel, delese utilizam para realizar as tarefas paraque foram desenvolvidos. Fechando tudoisso vem o Shell, que leva este nome
porque, em inglês, Shell significa con-cha, carapaça, isto é, fica entre o u-
suário e o sistema operacional, deforma que tudo que interage como sistema operacional, tem quepassar pelo seu crivo.
O ambiente ShellBom já que para chegar aonúcleo do Linux, no seu ker-nel que é o que interessa a
todo aplicativo, é necessária afiltragem do Shell, vamos enten-
der como ele funciona de forma atirar o máximo proveito das inú-
meras facilidades que ele nos oferece.O Linux, por definição, é um sistema
multiusuário – não podemos nunca nosesquecer disto – e para permitir o acessode determinados usuários e barrar a en-trada de outros, existe um arquivo cha-mado /etc/passwd, que além de fornecerdados para esta função de “leão-de-chá-cara” do Linux, também provê informa-ções para o início de uma sessão (ou“login”, para os íntimos) daqueles quepassaram por esta primeira barreira. Oúltimo campo de seus registros informaao sistema qual é o Shell que a pessoavai receber ao iniciar sua sessão.
Lembra que eu te falei de Shell, fa-mília, irmão? Pois é, vamos começar aentender isto: o Shell é a conceituaçãode concha envolvendo o sistema opera-cional propriamente dito, é o nomegenérico para tratar os filhos desta idéiaque, ao longo dos muitos anos de exis-
Diálogo entreouvido em uma mesade um botequim, entre umusuário de Linux e um empur-
rador de mouse:• Quem é o Bash?• É o filho caçula da família Shell.• Pô cara! Estás a fim de me deixar
maluco? Eu tinha uma dúvida e vocême deixa com duas!
• Não, maluco você já é há muito tem-po: desde que decidiu usar aquele sis-tema operacional que você precisareiniciar dez vezes por dia e ainda porcima não tem domínio nenhum sobreo que esta acontecendo no seu com-putador. Mas deixa isso prá lá, poisvou te explicar o que é Shell e os com-ponentes de sua família e ao final danossa conversa você dirá: “Meu Deusdo Shell! Porque eu não optei peloLinux antes?”.
82 Agosto 2004 www.linuxmagazine.com.br
Papo de BotequimLINUX USER
Você não agüenta mais aquele seu
amigo usuário de Linux enchendo o
seu saco com aquela história de que o
sistema é fantástico e o Shell é uma
ferramenta maravilhosa? A partir
desta edição vai ficar mais fácil en-
tender o porquê deste entusiasmo...
POR JULIO CEZAR NEVES
Curso de Shell Script
Papo de Botequim
Bourne Shell (sh): Desenvolvido por StephenBourne do Bell Labs (da AT&T, onde tambémfoi desenvolvido o Unix), foi durante muitosanos o Shell padrão do sistema operacionalUnix. É também chamado de Standard Shellpor ter sido durante vários anos o único, e éaté hoje o mais utilizado. Foi portado parapraticamente todos os ambientes Unix e dis-tribuições Linux.Korn Shell (ksh): Desenvolvido por DavidKorn, também do Bell Labs, é um supercon-junto do sh, isto é, possui todas as facilidades
do sh e a elas agregou muitas outras. A com-patibilidade total com o sh vem trazendomuitos usuários e programadores de Shellpara este ambiente.Boune Again Shell (bash):Desenvolvido ini-cialmente por Brian Fox e Chet Ramey, este éo Shell do projeto GNU. O número de seusadeptos é o que mais cresce em todo omundo, seja por que ele é o Shell padrão doLinux, seja por sua grande diversidade decomandos, que incorpora inclusive diversoscomandos característicos do C Shell.
C Shell (csh): Desenvolvido por Bill Joy, daUniversidade de Berkley, é o Shell mais uti-lizado em ambientes BSD. Foi ele quem intro-duziu o histórico de comandos. Aestruturação de seus comandos é bem simi-lar à da linguagem C. Seu grande pecado foiignorar a compatibilidade com o sh, partindopor um caminho próprio.Além destes Shells existem outros, mas ireifalar somente sobre os três primeiros, tratan-do-os genericamente por Shell e assinalandoas especificidades de cada um.
Quadro 1: Uma rapidinha nos principais sabores de Shell
tência do sistema operacional Unix,foram aparecendo. Atualmente existemdiversos sabores de Shell (veja Quadro 1na página anterior).
Como funciona o ShellO Shell é o primeiro programa que vocêganha ao iniciar sua sessão (se quiser-mos assassinar a língua portuguesapodemos também dizer “ao se logar”) noLinux. É ele quem vai resolver um montede coisas de forma a não onerar o kernelcom tarefas repetitivas, poupando-o paratratar assuntos mais nobres. Como cadausuário possui o seu próprio Shell inter-pondo-se entre ele e o Linux, é o Shellquem interpreta os comandos digitados eexamina as suas sintaxes, passando-osesmiuçados para execução.• Êpa! Esse negócio de interpretar co-
mando não tem nada a ver com inter-pretador não, né?
• Tem sim: na verdade o Shell é um in-terpretador que traz consigo uma po-derosa linguagem com comandos dealto nível, que permite construção deloops, de tomadas de decisão e de ar-mazenamento de valores em variáveis,como vou te mostrar.
• Vou explicar as principais tarefas que oShell cumpre, na sua ordem de exe-cução. Preste atenção, porque estaordem é fundamental para o entendi-mento do resto do nosso bate papo.
Análise da linha de comandoNeste exame o Shell identifica os carac-teres especiais (reservados) que têm sig-nificado para a interpretação da linha elogo em seguida verifica se a linha pas-sada é um comando ou uma atribuiçãode valores, que são os ítens que voudescrever a seguir.
ComandoQuando um comando é digi-tado no “prompt” (ou linha decomando) do Linux, ele é divi-dido em partes, separadas porespaços em branco: a primeiraparte é o nome do programa,cuja existência será verificada;em seguida, nesta ordem, vêmas opções/parâmetros, redire-cionamentos e variáveis.
Quando o programa identifi-cado existe, o Shell verifica aspermissões dos arquivos en-
volvidos (inclusive o próprio programa),e retorna um erro caso o usuário quechamou o programa não esteja autor-izado a executar esta tarefa.
$ ls linux
linux
Neste exemplo o Shell identificou o ls co-mo um programa e o linux como um pa-râmetro passado para o programa ls.
AtribuiçãoSe o Shell encontra dois campos separa-dos por um sinal de igual (=) sem espa-ços em branco entre eles, ele identificaesta seqüência como uma atribuição.
$ valor=1000
Neste caso, por não haver espaços embranco (que é um dos caracteres reserva-dos), o Shell identificou uma atribuição ecolocou 1000 na variável valor.
Resolução deRedirecionamentosApós identificar os componentes da li-nha que você digitou, o Shell parte paraa resolução de redirecionamentos.
O Shell tem incorporado ao seu elencode habilidades o que chamamos de
redirecionamento, que pode ser deentrada (stdin), de saída (stdout) ou doserros (stderr), conforme vou explicar aseguir. Mas antes precisamos falar de...
Substituição de VariáveisNeste ponto, o Shell verifica se as even-tuais variáveis (parâmetros começadospor $), encontradas no escopo docomando, estão definidas e as substituipor seus valores atuais.
Substituição de Meta-CaracteresSe algum meta-caracter (ou “coringa”,como *, ? ou []) for encontrado na linhade comando, ele será substituído porseus possíveis valores.
Supondo que o único item no seudiretório corrente cujo nome começacom a letra n seja um diretório chamadonomegrandeprachuchu, se você fizer:
$ cd n*
como até aqui quem está manipulando alinha de comando ainda é o Shell e oprograma cd ainda não foi executado, oShell expande o n* para nomegrandepra-
chuchu (a única possibilidade válida) eexecuta o comando cd com sucesso.
Entrega da linha de comandopara o kernelCompletadas todas as tarefas anteriores,o Shell monta a linha de comando, jácom todas as substituições feitas echama o kernel para executá-la em umnovo Shell (Shell filho), que ganha umnúmero de processo (PID ou ProcessIDentification) e fica inativo, tirandouma soneca durante a execução do pro-grama. Uma vez encerrado este processo(e o Shell filho), o “Shell pai” recebenovamente o controle e exibe um“prompt”, mostrando que está prontopara executar outros comandos.
83www.linuxmagazine.com.br Agosto 2004
LINUX USERPapo de Botequim
Figura 1: Ambiente em camadas de um sistema Linux
Shell
Programas e Comandos
Núcleo ou Kernel
Hardware
Quando digo que o último campo do arqui-vo /etc/passwd informa ao sistema qual é oShell que o usuário vai usar ao se “logar”, istodeve ser interpretado ao pé-da-letra. Se estecampo do seu registro contém o termo prog,ao acessar o sistema o usuário executará oprograma prog. Ao término da execução, asessão do usuário se encerra automatica-mente. Imagine quanto se pode incremen-tar a segurança com este simples artifício.
Com que Shell eu vou?
Jamais faça:
$ valor = 1000bash: valor: not found
Neste caso, o Bash achou a palavra valor iso-lada por espaços e julgou que você estivessemandando executar um programa chama-do valor, para o qual estaria passando doisparâmetros: = e 1000.
Cuidado na Atribuição
$ echo \*
$ echo *
Viu a diferença?• Aspas (“): exatamente iguais ao após-
trofo, exceto que, se a cadeia entreaspas contiver um cifrão ($), umacrase (`), ou uma barra invertida (\),estes caracteres serão interpretadospelo Shell.Não precisa se estressar, eu não te deiexemplos do uso das aspas por quevocê ainda não conhece o cifrão ($)nem a crase (`). Daqui para frente -veremos com muita constância o usodestes caracteres especiais; o maisimportante é entender seu significado.
Caracteres deredirecionamentoA maioria dos comandos tem uma entra-da, uma saída e pode gerar erros. Estaentrada é chamada Entrada Padrão oustdin e seu dispositivo padrão é o tecladodo terminal. Analogamente, a saída docomando é chamada Saída Padrão oustdout e seu dispositivo padrão é a telado terminal. Para a tela também sãoenviadas normalmente as mensagens deerro oriundas dos comandos, chamadaneste caso de Saída de Erro Padrão oustderr. Veremos agora como alterar esteestado de coisas.
Vamos fazer um programa gago. Paraisto digite (tecle “Enter” ao final de cadalinha – comandos do usuário são ilus-trados em negrito):
$ cat
E-e-eu sou gago. Vai encarar?
E-e-eu sou gago. Vai encarar?
O cat é um comando que lista o con-teúdo do arquivo especificado para aSaída Padrão (stdout). Caso a entradanão seja definida, ele espera os dados dastdin (a entrada padrão). Ora como eunão especifiquei a entrada, ele a está
esperando pelo teclado (Entrada Padrão)e como também não citei a saída, o queeu teclar irá para a tela (Saída Padrão),criando desta forma – como eu haviaproposto – um programa gago. Experi-mente!
Redirecionamentop da SaídaPadrãoPara especificarmos a saída de um pro-grama usamos o símbolo “>” ou o“>>”, seguido do nome do arquivo pa-ra o qual se deseja mandar a saída.
Vamos transformar o programa ante-rior em um “editor de textos”:
$ cat > Arq
O cat continua sem ter a entrada especi-ficada, portanto está aguardando que osdados sejam teclados, porém a sua saídaestá sendo desviada para o arquivo Arq.Assim sendo, tudo que esta sendo tecla-do esta indo para dentro de Arq, de for-ma que fizemos o editor de textos maiscurto e ruim do planeta.
Se eu fizer novamente:
$ cat > Arq
Os dados contidos em Arq serão perdi-dos, já que antes do redirecionamento oShell criará um Arq vazio. Para colocarmais informações no final do arquivo eudeveria ter feito:
$ cat >> Arq
Redirecionamento da Saídade Erro PadrãoAssim como por padrão o Shell recebe osdados do teclado e envia a saída para atela, os erros também vão para a tela sevocê não especificar para onde eles de-vem ser enviados. Para redirecionar oserros, use 2> SaidaDeErro. Note que en-tre o número 2 e o sinal de maior (>)não existe espaço em branco.
Vamos supor que durante a execuçãode um script você pode, ou não (depen-dendo do rumo tomado pela execuçãodo programa), ter criado um arquivochamado /tmp/seraqueexiste$$. Comonão quer ficar com sujeira no discorígido, ao final do script você coloca alinha a seguir:
rm /tmp/seraqueexiste$$
Decifrando a Pedra de RosetaPara tirar aquela sensação que você temquando vê um script Shell, que maisparece uma sopa de letrinhas ou um con-junto de hieróglifos, vou lhe mostrar osprincipais caracteres especiais para quevocê saia por aí como Champollion deci-frando a Pedra de Roseta.
Caracteres para remoção dosignificado.É isso mesmo, quando não desejamosque o Shell interprete um caractereespecífico, devemos “escondê-lo” dele.Isso pode ser feito de três maneiras difer-entes, cada uma com sua peculiaridade:• Apóstrofo (´): quando o Shell vê uma
cadeia de caracteres entre apóstrofos,ele retira os apóstrofos da cadeia e nãointerpreta seu conteúdo.
$ ls linuxm*
linuxmagazine
$ ls 'linuxm*'
bash: linuxm* no such file U
or directory
No primeiro caso o Shell “expandiu” oasterisco e descobriu o arquivo linux-
magazine para listar. No segundo, osapóstrofos inibiram a interpretação doShell e veio a resposta que não existe oarquivo linuxm*.
• Contrabarra ou Barra Invertida (\): i-dêntico aos apóstrofos exceto que abarra invertida inibe a interpretaçãosomente do caractere que a segue.Suponha que você, acidentalmente,tenha criado um arquivo chamado *
(asterisco) – o que alguns sabores deUnix permitem – e deseja removê-lo.Se você fizesse:
$ rm *
Você estaria na maior encrenca, pois orm removeria todos os arquivos dodiretório corrente. A melhor forma defazer o serviço é:
$ rm \*
Desta forma, o Shell não interpreta oasterisco, evitando a sua expansão.Faça a seguinte experiência científica:
$ cd /etc
$ echo '*'
84 Agosto 2004 www.linuxmagazine.com.br
Papo de BotequimLINUX USER
Como já havia dito, o Shell resolve a linha edepois manda o comando para a execução.Assim, se você redirecionar a saída de umarquivo para ele próprio, primeiramente oShell “esvazia”este arquivo e depois mandao comando para execução! Desta forma,para sua alegria, você acabou de perder oconteúdo de seu querido arquivo.
Redirecionamento Perigoso
Caso o arquivo não existisse seria envi-ado para a tela uma mensagem de erro.Para que isso não aconteça faça:
rm /tmp/seraqueexiste$$ 2> U
/dev/null
Para que você teste a Saída de Erro Pa-drão direto no prompt do seu Shell, voudar mais um exemplo. Faça:
$ ls naoexiste
bash: naoexiste no such file U
or directory
$ ls naoexiste 2> arquivodeerros
$
$ cat arquivodeerros
bash: naoexiste no such file U
or directory
Neste exemplo, vimos que quando fize-mos um ls em naoexiste, ganhamos umamensagem de erro. Após redirecionar aSaída de Erro Padrão para arquivodeerros
e executar o mesmo comando, recebe-mos somente o “prompt” na tela. Quan-do listamos o conteúdo do arquivo parao qual foi redirecionada a Saída de ErroPadrão, vimos que a mensagem de errotinha sido armazenada nele.
É interessante notar que estes carac-teres de redirecionamento são cumula-tivos, isto é, se no exemplo anteriorfizéssemos o seguinte:
$ ls naoexiste 2>> U
arquivodeerros
a mensagem de erro oriunda do ls seriaanexada ao final de arquivodeerros.
Redirecionamento daEntrada PadrãoPara fazermos o redirecionamento da En-trada Padrão usamos o < (menor que).“E pra que serve isso?”, você vai me per-guntar. Deixa eu dar um exemplo, quevocê vai entender rapidinho.
Suponha que você queira mandar ummail para o seu chefe. Para o chefe nós
caprichamos, né? Então ao invés de sairredigindo o mail direto no “prompt”, deforma a tornar impossível a correção deuma frase anterior onde, sem querer,você escreveu um “nós vai”, você editaum arquivo com o conteúdo da mensa-gem e após umas quinze verificaçõessem constatar nenhum erro, decideenviá-lo e para tal faz:
$ mail [email protected] < UU
arquivocommailparaochefe
e o chefe receberá uma mensagem com oconteúdo do arquivocommailparaochefe.
Outro tipo de redirecionamento “muitolouco” que o Shell permite é o chamado“here document”. Ele é representado por<< e serve para indicar ao Shell que oescopo de um comando começa na linhaseguinte e termina quando encontra umalinha cujo conteúdo seja unicamente o“label” que segue o sinal <<.
Veja o fragmento de script a seguir,com uma rotina de ftp:
ftp -ivn hostremoto << fimftp
user $Usuario $Senha
binary
get arquivoremoto
fimftp
neste pedacinho de programa temos ummonte de detalhes interessantes:• As opções usadas para o ftp (-ivn)
servem para ele listar tudo que estáacontecendo (opção -v de “verbose”),para não ficar perguntando se vocêtem certeza que deseja transmitir cadaarquivo (opção -i de “interactive”) efinalmente a opção -n serve para dizerao ftp para ele não solicitar o usuário esua senha, pois estes serão informadospela instrução específica (user);
• Quando eu usei o << fimftp, estavadizendo o seguinte para o interpreta-dor: “Olha aqui Shell, não se meta em
nada a partir deste ponto até encontraro ‘label’ fimftp. Você não entenderiadroga nenhuma, já que são instruçõesespecíficas do ftp”.
Se fosse só isso seria simples, maspelo próprio exemplo dá para ver queexistem duas variáveis ($Usuario e$Senha), que o Shell vai resolver antesdo redirecionamento. Mas a grandevantagem deste tipo de construção éque ela permite que comandos tam-bém sejam interpretados dentro doescopo do “here document”, o que,aliás, contraria o que acabei de dizer.Logo a seguir te explico como essenegócio funciona. Agora ainda não dá,estão faltando ferramentas.
• O comando user é do repertório deinstruções do ftp e serve para passar ousuário e a senha que haviam sidolidos em uma rotina anterior a estefragmento de código e colocados res-pectivamente nas duas variáveis:$Usuario e $Senha.
• O binary é outra instrução do ftp, queserve para indicar que a transferênciade arquivoremoto será feita em modobinário, isto é o conteúdo do arquivonão será inteerpretado para saber seestá em ASCII, EBCDIC, …
• O comando get arquivoremoto diz aocliente ftp para pegar este arquivo noservidor hostremoto e trazê-lo para anossa máquina local. Se quiséssemosenviar um arquivo, bastaria usar, porexemplo, o comando put arquivolocal.
Redirecionamento decomandosOs redirecionamentos de que falamos atéagora sempre se referiam a arquivos, istoé, mandavam para arquivo, recebiam dearquivo, simulavam arquivo local, … Oque veremos a partir de agora, redirecio-na a saída de um comando para a entra-da de outro. É utilíssimo e, apesar de nãoser macaco gordo, sempre quebra os
85www.linuxmagazine.com.br Agosto 2004
LINUX USERPapo de Botequim
Preste atenção! Não confunda >> com 2>. Oprimeiro anexa dados ao final de um arqui-vo, e o segundo redireciona a Saída de ErroPadrão (stderr) para um arquivo que estásendo designado. Isto é importante!
Dados ou Erros?
O $$ contém o PID, isto é,o número do seuprocesso.Como o Linux é multiusuário,ébom anexar sempre o $$ ao nome dos seusarquivos para não haver problema de propri-edade, isto é,caso você batizasse o seu ar-quivo simplesmente como seraqueexiste,aprimeira pessoa que o usasse (criando-oentão) seria o seu dono e a segunda ganhariaum erro quando tentasse gravar algo nele.
Direito de Posse
Um erro comum no uso de labels (como o
fimftp do exemplo anterior) é causado pela
presença de espaços em branco antes ou
após o mesmo. Fique muito atento quanto a
isso, por que este tipo de erro costuma dar
uma boa surra no programador, até que seja
detectado. Lembre-se: um label que se preze
tem que ter uma linha inteira só para ele.
Etiquetas Erradas
$ echo "Existem who | wc -l UU
usuarios conectados"
Existem who | wc -l usuarios U
conectados
Hi! Olha só, não funcionou! É mesmo,não funcionou e não foi por causa dasaspas que eu coloquei, mas sim por queeu teria que ter executado o who | wc -l
antes do echo. Para resolver este proble-ma, tenho que priorizar a segunda partedo comando com o uso de crases:
$ echo "Existem `who | wc -l` UU
usuarios conectados"
Existem 8 usuarios U
conectados
Para eliminar esse monte de brancosantes do 8 que o wc -l produziu, bastaretirar as aspas. Assim:
$ echo Existem `who | wc -l` UU
usuarios conectados
Existem 8 usuarios conectados
As aspas protegem da interpretação doShell tudo que está dentro dos seus lim-ites. Como para o Shell basta um espaçoem branco como separador, o monte deespaços será trocado por um único apósa retirada das aspas.
Outra coisa interessante é o uso doponto-e-vírgula. Quando estiver no Shell,você deve sempre dar um comando emcada linha. Para agrupar comandos emuma mesma linha, temos que separá-lospor ponto-e-vírgula. Então:
$ pwd ; cd /etc; pwd ;cd -;pwd
/home/meudir
/etc
/home/meudir
Neste exemplo, listei o nome do diretóriocorrente com o comando pwd, mudeipara o diretório /etc, novamente listei onome do diretório e finalmente voltei pa-ra o diretório onde estava anteriormente(cd -), listando seu nome. Repare quecoloquei o ponto-e-vírgula de todas asformas possíveis, para mostrar que nãoimporta se existem espaços em brancoantes ou após este caracter.
Finalmente, vamos ver o caso dosparênteses. No exemplo a seguir, colo-camos diversos comandos separados porponto-e-vírgula entre parênteses:
$ (pwd ; cd /etc ; pwd)
/home/meudir
/etc
$ pwd
/home/meudir
“Quequeiiisso” minha gente? Eu estavano /home/meudir, mudei para o /etc,constatei que estava neste diretório como pwd seguinte e quando o agrupamentode comandos terminou, eu vi que conti-nuava no /etc/meudir!
Hi! Será que tem coisa do mágicoMandrake por aí? Nada disso. O interes-sante do uso de parênteses é que elesinvocam um novo Shell para executar oscomandos que estão em seu interior.Desta forma, fomos realmente para odiretório /etc, porém após a execução detodos os comandos, o novo Shell queestava no diretório /etc morreu e retor-namos ao Shell anterior que estava em/home/meudir.
Que tal usar nossos novos conceitos?
$ mail [email protected] << FIM
Ola suporte, hoje as `date UU
“+%hh:mm”` ocorreu novamente UU
aquele problema que eu havia UU
reportado por telefone. De UU
acordo com seu pedido segue a UU
listagem do diretorio:
`ls -l`
Abracos a todos.
FIM
Finalmente agora podemos demonstrar oque conversamos anteriormente sobre“here document”. Os comandos entrecrases tem prioridade, portanto o Shellos executará antes do redirecionamentodo “here document”. Quando o suportereceber a mensagem, verá que oscomandos date e ls foram executadosantes do comando mail, recebendo entãoum instantâneo do ambiente nomomento de envio do email.- Garçom, passa a régua! ■
maiores galhos. Seu nome é “pipe” (queem inglês significa tubo, já que ele cana-liza a saída de um comando para aentrada de outro) e sua representação é a| (barra vertical).
$ ls | wc -l
21
O comando ls passou a lista de arquivospara o comando wc, que quando estácom a opção -l conta a quantidade de li-nhas que recebeu. Desta forma, pode-mos afirmar categoricamente que nomeu diretório existiam 21 arquivos.
$ cat /etc/passwd | sort | lp
A linha de comandos acima manda alistagem do arquivo /etc/passwd para aentrada do comando sort. Este a classi-fica e envia para o lp que é o gerenciadorda fila de impressão.
Caracteres de ambienteQuando queremos priorizar uma expres-são, nós a colocamos entre parênteses,não é? Pois é, por causa da aritmética énormal pensarmos deste jeito. Mas emShell o que prioriza mesmo são as crases(`) e não os parênteses. Vou dar exemp-los para você entender melhor.
Eu quero saber quantos usuários estão“logados” no computador que eu admi-nistro. Eu posso fazer:
$ who | wc -l
8
O comando who passa a lista de usuáriosconectados ao sistema para o comandowc -l, que conta quantas linhas recebeu emostra a resposta na tela. Muito bem,mas ao invés de ter um número oitosolto na tela, o que eu quero mesmo éque ele esteja no meio de uma frase. Ora,para mandar frases para a tela eu só pre-ciso usar o comando echo; então vamosver como é que fica:
86 Agosto 2004 www.linuxmagazine.com.br
Papo de BotequimLINUX USER
Julio Cezar Neves é Analista de Su-porte de Sistemas desde 1969 e tra-balha com Unix desde 1980, quandofez parte da equipe que desenvolveuo SOX, sistema operacional, similarao Unix, da Cobra Computadores. Éprofessor do curso de Mestrado emSoftware Livre das Faculdades Estáciode Sá, no Rio de Janeiro.SO
BREOAUTO
R
Em Unix existe um arquivo fantasma.Chama-se /dev/null.Tudo que é enviadopara este arquivo some. Assemelha-se a umBuraco Negro. No caso do exemplo, comonão me interessava guardar a possível men-sagem de erro oriunda do comando rm, redi-recionei-a para este arquivo.
Buraco Negro
LINUX USERPapo de Botequim
87Setembro 2004www.linuxmagazine.com.br
$ grep franklin /etc/passwd
Pesquisando em vários arquivos:
$ grep grep *.sh
Pesquisando na saída de um comando:
$ who | grep carvalho
No 1º exemplo, procurei a palavrafranklin em qualquer lugar do arquivo/etc/passwd. Se quisesse procurar umnome de usuário, isto é, somente no iní-cio dos registros desse arquivo, poderiadigitar $ grep ‘^franklin’ /etc/passwd.
“E para que servem o circunflexo e osapóstrofos?”, você vai me perguntar. Setivesse lido os artigos que mencionei,saberia que o circunflexo serve para limi-tar a pesquisa ao início de cada linha eos apóstrofos servem para o Shell nãointerpretar esse circunflexo, deixando-opassar incólume para o comando grep.
No 2º exemplo mandei listar todas as
linhas que usavam a palavra grep, emtodos os arquivos terminados em .sh.Como uso essa extensão para definirmeus arquivos com programas em Shell,malandramente, o que fiz foi listar as lin-has dos programas que poderia usarcomo exemplo do comando grep.
Olha que legal! O grep aceita comoentrada a saída de outro comando, redi-recionado por um pipe (isso é muito
comum em Shell e é um tremendoacelerador da execução de coman-dos). Dessa forma, no 3° exemplo,o comando who listou as pessoas“logadas” na mesma máquina quevocê (não se esqueça jamais: oLinux é multiusuário) e o grep foiusado para verificar se o Carvalho
estava trabalhando ou “coçando”.O grep é um comando muito con-
hecido, pois é usado com muita fre-qüência. O que muitas pessoas não
sabem é que existem três comandos nafamília grep: grep, egrep e fgrep. A princi-pais diferenças entre os 3 são:• grep - Pode ou não usar expressões
regulares simples, porém no caso denão usá-las, o fgrep é melhor, por sermais rápido.
• egrep (“e” de extended, estendido) - Émuito poderoso no uso de expressõesregulares. Por ser o mais poderoso dostrês, só deve ser usado quando fornecessária a elaboração de umaexpressão regular não aceita pelo grep.
• fgrep (“f” de fast, rápido) - Como onome diz, é o ligeirinho da família,executando o serviço de forma muitoveloz (por vezes é cerca de 30% maisrápido que o grep e 50% mais que oegrep), porém não permite o uso deexpressões regulares na pesquisa.
–Agora que você já conhece as difer-enças entre os membros da família, mediga: o que você acha dos três exemplosque eu dei antes das explicações?– Achei que o fgrep resolveria o teu prob-
lema mais rapidamente que o grep.– Perfeito! Tô vendo que você está
atento, entendendo tudo que estou teexplicando! Vamos ver mais exemplos
Garçom! Traz um “chops” e dois“pastel”. O meu amigo hoje nãovai beber porque está finalmente
sendo apresentado a um verdadeiro sis-tema operacional, e ainda tem muitacoisa a aprender!– E então, amigo, tá entendendo
tudo que te expliquei até agora?– Entendendo eu tô, mas não vi
nada prático nisso…– Calma rapaz, o que te falei até
agora serve como base ao que háde vir daqui pra frente. Vamos usaressas ferramentas que vimos paramontar programas estruturados. Vocêverá porque até na TV já teve pro-grama chamado “O Shell é o Limite”.Para começar vamos falar dos coman-dos da família grep
– Grep? Não conheço nenhum termo eminglês com este nome…
– É claro, grep é um acrônimo (sigla)para Global Regular Expression Print,que usa expressões regulares parapesquisar a ocorrência de cadeias decaracteres na entrada definida.Por falar em expressões regulares (ou
regexp), o Aurélio Marinho Jargas es-creveu dois artigos [1 e 2] imperdíveispara a Revista do Linux sobre esseassunto e também publicou um livro [3]pela Editora Novatec. Acho bom você leresses artigos, eles vão te ajudar no queestá para vir.
Eu fico com grep,você com gripeEsse negócio de gripe é brincadeira, sóum pretexto para pedir umas caipirinhas.Eu te falei que o grep procura cadeias decaracteres dentro de uma entrada defi-nida, mas o que vem a ser uma “entradadefinida”? Bem, existem várias formasde definir a entrada do comando grep.Veja só. Para pesquisar em um arquivo:
Nossos personagens voltam à mesa do bar para discutir expressões regulares e
colocar a “mão na massa” pela primeira vez, construindo um aplicativo simples
para catalogar uma coleção de CDs. POR JÚLIO CÉSAR NEVES
Curso de Shell Script
Papo de Botequim - Parte II
Papo de BotequimLINUX USER
88 Setembro 2004 www.linuxmagazine.com.br
para clarear de vez as diferenças deuso entre os membros da família.Eu sei que em um arquivo qualquer
existe um texto falando sobre Linux, sónão tenho certeza se está escrito com Lmaiúsculo ou minúsculo. Posso fazeruma busca de duas formas:
egrep (Linux | linux) arquivo.txt
ou então:
grep [Ll]inux arquivo.txt
No primeiro caso, a expressão regularcomplexa (Linux | linux) usa os parênte-ses para agrupar as opções e a barra ver-tical (|) é usada como um “ou” (or, eminglês) lógico, isto é, estou procurandoLinux ou linux.
No segundo, a expressão regular[Ll]inux significa: começado por L ou lseguido de inux. Como esta é umaexpressão simples, o grep consegueresolvê-la, por isso é melhor usar asegunda forma, já que o egrep tornaria apesquisa mais lenta.
Outro exemplo. Para listar todos ossubdiretórios do diretório corrente, bastausar o comando $ ls -l | grep ‘^d’. Veja oresultado no Quadro 1.
No exemplo, o circunflexo (^) serviupara limitar a pesquisa à primeiraposição da saída do ls longo (parâmetro
-l). Os apóstrofos foram usados para oShell não “ver” o circunflexo. Vamos vermais um. Veja na Tabela 1 as quatroprimeiras posições possíveis da saída deum ls -l em um arquivo comum (não édiretório, nem link, nem …).
Para descobrir todos os arquivos exe-cutáveis em um determinado diretórioeu poderia fazer:
$ ls -la | egrep ‘^-..(x|s)’
novamente usamos o circunflexo paralimitar a pesquisa ao início de cadalinha, ou seja, listamos as linhas quecomeçam por um traço (-), seguido dequalquer coisa (o ponto), novamenteseguido de qualquer coisa, e por fim umx ou um s. Obteríamos o mesmo resul-tado se usássemos o comando:
$ ls -la | grep ‘^-..[xs]’
e além disso, agilizaríamos a pesquisa.
A “CDteca”Vamos começar a desenvolver progra-mas! Creio que a montagem de umbanco de dados de músicas é bacanapara efeito didático (e útil nestes temposde downloads de arquivos MP3 equeimadores de CDs). Não se esqueçaque, da mesma forma que vamos desen-volver um monte de programas paraorganizar os seus CDs de música, compequenas adaptações você pode fazer omesmo para organizar os CDs de soft-ware que vêm com a Linux Magazine eoutros que você compra ou queima, edisponibilizar esse banco de softwarepara todos os que trabalham com você(o Linux é multiusuário, e como tal deve
ser explorado).– Péra aí! De onde eu vou receber os
dados dos CDs?– Vou mostrar como o programa pode
receber parâmetros de quem o estiverexecutando e, em breve, ensinarei a leros dados da tela ou de um arquivo.
Passando parâmetrosVeja abaixo a estrutura do arquivo con-tendo a lista das músicas:
nomedoálbum^intérprete1~nomeU
damúsica1:...:intérpreten~nomeU
damúsican
Isto é, o nome do álbum será separadopor um circunflexo do resto do registro,formado por diversos grupos compostospelo intérprete de cada música do CD e amúsica interpretada. Estes grupos sãoseparados entre si por dois pontos (:) e,internamente, o intérprete será separadopor um til (~) do nome da música.
Quero escrever um programa chamadomusinc, que incluirá registros no meuarquivo músicas. Passarei cada álbumcomo parâmetro para o programa:
$ musinc “álbum^interprete~U
musica:interprete~musica:...”
Desta forma, musinc estará recebendo osdados de cada álbum como se fosse umavariável. A única diferença entre umparâmetro recebido e uma variável é queos primeiros recebem nomes numéricos(o que quis dizer é que seus nomes sãoformados somente por um algarismo,isto é, $1, $2, $3, …, $9). Vamos, fazermais alguns testes:
$ cat teste
#!/bin/bash
#Teste de passagem de parametros
echo “1o. parm -> $1”
echo “2o. parm -> $2”
echo “3o. parm -> $3”
Agora vamos rodar esse programinha:
$ teste passando parametros para U
testar
bash: teste: cannot execute
Ops! Esqueci-me de tornar o script exe-cutável. Vou fazer isso e testar nova-mente o programa:
$ ls -l | grep ‘^d’
drwxr-xr-x 3 root root 4096 Dec 18 2000 doc
drwxr-xr-x 11 root root 4096 Jul 13 18:58 freeciv
drwxr-xr-x 3 root root 4096 Oct 17 2000 gimp
drwxr-xr-x 3 root root 4096 Aug 8 2000 gnome
drwxr-xr-x 2 root root 4096 Aug 8 2000 idl
drwxrwxr-x 14 root root 4096 Jul 13 18:58 locale
drwxrwxr-x 12 root root 4096 Jan 14 2000 lyx
drwxrwxr-x 3 root root 4096 Jan 17 2000 pixmaps
drwxr-xr-x 3 root root 4096 Jul 2 20:30 scribus
drwxrwxr-x 3 root root 4096 Jan 17 2000 sounds
drwxr-xr-x 3 root root 4096 Dec 18 2000 xine
drwxr-xr-x 3 root root 4096 Jun 19 2000 xplns
Quadro 1 - Listando subdiretórios
Posição Valores possíveis
1ª -
2ª r ou -
3ª w ou -
4ª x, s(suid) ou -
Tabela 1
LINUX USERPapo de Botequim
89Setembro 2004www.linuxmagazine.com.br
Execute o programa:
$ teste passando parametros para testar
O programa teste recebeu 4 U
parametros
1o. parm -> passando
2o. parm -> parametros
3o. parm -> para
Para listar todos de uma U
“tacada” eu faco passando U
parametros para testar
Repare que antes das aspas usei umabarra invertida, para escondê-las dainterpretação do Shell (se não usasse ascontrabarras as aspas não apareceriam).
Como disse, os parâmetros recebemnúmeros de 1 a 9, mas isso não significaque não posso usar mais de noveparâmetros. Significa que só possoendereçar nove. Vamos testar isso:
$ cat teste
#!/bin/bash
# Programa para testar passagem U
de parametros (3a. Versao)
echo O programa $0 recebeu $# U
parametros
echo “11o. parm -> $11”
shift
echo “2o. parm -> $1”
shift 2
echo “4o. parm -> $1”
Execute o programa:
$ teste passando parametros para U
testar
O programa teste recebeu 4 U
parametros que são:
11o. parm -> passando1
2o. parm -> parametros
4o. parm -> testar
Duas coisas muito interessantes aconte-ceram neste script. Para mostrar que osnomes dos parâmetros variam de $1 a $9
digitei echo $11 e o que aconteceu? OShell interpretou como sendo $1 seguidodo algarismo 1 e listou passando1;
O comando shift, cuja sintaxe é shift n,podendo o n assumir qualquer valornumérico, despreza os n primeirosparâmetros, tornando o parâmetro deordem n+1.
Bem, agora que você já sabe sobrepassagem de parâmetros, vamos voltar ànossa “cdteca” para fazer o script de
inclusão de CDs no meu banco chamadomusicas. O programa é muito simples(como tudo em Shell). Veja a Listagem 1.
O script é simples e funcional; limito-me a anexar ao fim do arquivo musicas oparâmetro recebido. Vamos cadastrar 3álbuns para ver se funciona (para nãoficar “enchendo lingüiça,” suponho queem cada CD só existem duas músicas):
$ musinc “album3^Artista5U
~Musica5:Artista6~Musica5”
$ musinc “album1^Artista1U
~Musica1:Artista2~Musica2”
$ musinc “album 2^Artista3U
~Musica3:Artista4~Musica4”
Listando o conteúdo do arquivo musicas:
$ cat musicas
album3^Artista5~Musica5:Artista6U
~Musica6
album1^Artista1~Musica1:Artista2U
~Musica2
album2^Artista3~Musica3:Artista4U
~Musica4
Podia ter ficado melhor. Os álbuns estãofora de ordem, dificultando a pesquisa.Vamos alterar nosso script e depois testá-lo novamente. Veja a listagem 2. Sim-plesmente inseri uma linha que classificao arquivo musicas, redirecionando asaída para ele mesmo (para isso serve aopção -o), após cada álbum ser anexado.
$ cat musicas
album1^Artista1~Musica1:Artista2U
~Musica2
albu2^Artista3~Musica3:Artista4U
~Musica4
album3^Artista5~Musica5:Artista6U
~Musica6
Oba! Agora o programa está legal equase funcional. Ficará muito melhor emuma nova versão, que desenvolveremosapós aprender a adquirir os dados da telae formatar a entrada.
$ chmod 755 teste
$ teste passando parametros para U
testar
1o. parm -> passando
2o. parm -> parametros
3o. parm -> para
Repare que a palavra testar, que seria oquarto parâmetro, não foi listada. Issoocorreu porque o programa teste só listaos três primeiros parâmetros recebidos.Vamos executá-lo de outra forma:
$ teste “passando parametros” U
para testar
1o. parm -> passando parametros
2o. parm -> para
3o. parm -> testar
As aspas não deixaram o Shell ver oespaço em branco entre as duasprimeiras palavras, e elas foram consid-eradas como um único parâmetro. Efalando em passagem de parâmetros,uma dica: veja na Tabela 2 algumas var-iáveis especiais. Vamos alterar o pro-grama teste para usar as novas variáveis:
$ cat teste
#!/bin/bash
# Programa para testar passagem U
de parametros (2a. Versao)
echo O programa $0 recebeu $# U
parametros
echo “1o. parm -> $1”
echo “2o. parm -> $2”
echo “3o. parm -> $3”
echo Para listar todos de uma U
\”tacada\” eu faco $*
Variável Significado
$0 Contém o nome do programa
$# Contém a quantidade de parâmetros passados
$* Contém o conjunto de todos os parâmetros (muito parecido com $@)
Tabela 2: Variáveis especiais$ cat musinc
#!/bin/bash
# Cadastra CDs (versao 2)
#
echo $1 >> musicas
sort -o musicas musicas
Listagem 2
$ cat musinc
#!/bin/bash
# Cadastra CDs (versao 1)
#
echo $1 >> musicas
Listagem 1: Incluindo CDsna “CDTeca”
mando. Estamos então prontos paradesenvolver o script para remover CDsempenados da sua “CDteca”. Veja ocódigo da Listagem 5.
Na primeira linha mandei para/tmp/mus$$ o arquivo musicas, sem osregistros que atendessem a consulta feitapelo comando grep. Em seguida, movi/tmp/mus$$ por cima do antigo musicas.Usei o arquivo /tmp/mus$$ como arqui-vo de trabalho porque, como já haviacitado no artigo anterior, o $$ contém oPID (identificação do processo) e, dessaforma, cada um que editar o arquivomusicas o fará em um arquivo de tra-balho diferente, evitando colisões.
Os programas que fizemos até aquiainda são muito simples, devido à faltade ferramentas que ainda temos. Mas ébom praticar os exemplos dados porque,eu prometo, chegaremos a desenvolverum sistema bacana para controle dosseus CDs. Na próxima vez que nosencontrarmos, vou te ensinar como fun-cionam os comandos condicionais eaprimoraremos mais um pouco essesscripts. Por hoje chega! Já falei demais eestou de goela seca! Garçom! Mais umsem colarinho! ■
Papo de BotequimLINUX USER
90 Setembro 2004 www.linuxmagazine.com.br
Julio Cezar Neves éAnalista de Suporte deSistemas desde 1969 etrabalha com Unixdesde 1980, quandofez parte da equipeque desenvolveu oSOX, um sistemaoperacional similar ao Unix,produzidopela Cobra Computadores.SO
BREOAUTO
R
Ficar listando arquivos com ocomando cat não está com nada, vamosfazer um programa chamado muslist
para listar um álbum, cujo nome serápassado como parâmetro. Veja o códigona Listagem 3:
Vamos executá-lo, procurando peloalbum 2. Como já vimos antes, para pas-sar a string album 2 é necessário pro-tegê-la da interpretação do Shell, paraque ele não a interprete como doisparâmetros. Exemplo:
$ muslist “album 2”
grep: can’t open 2
musicas: album1^Artista1~Musica1U
:Artista2~Musica2
musicas: album2^Artista3~Musica3U
:Artista4~Musica4
musicas:album3^Artista5~Musica5U
:Artista6~Musica6
Que lambança! Onde está o erro? Eu tiveo cuidado de colocar o parâmetro pas-sado entre aspas para o Shell não odividir em dois! É, mas repare como ogrep está sendo executado:
grep $1 musicas
Mesmo colocando álbum 2 entre aspas,para que fosse encarado como um únicoparâmetro, quando o $1 foi passado peloShell para o comando grep, transformou-se em dois argumentos. Dessa forma, oconteúdo da linha que o grep executoufoi o seguinte:
grep album 2 musicas
Como a sintaxe do grep é:
grep <cadeia de caracteres> U
[arq1, arq2, ..., arqn]
O grep entendeu que deveria procurar acadeia de caracteres album nos arquivos2 e musicas. Como o arquivo 2 nãoexiste, grep gerou o erro e, por encontrara palavra album em todos os registros demusicas, listou a todos.
É melhor ignorarmos maiúsculas eminúsculas na pesquisa. Resolveremosos dois problemas com a Listagem 4.
Nesse caso, usamos a opção -i do grepque, como já vimos, serve para ignorarmaiúsculas e minúsculas, e colocamos o$1 entre aspas, para que o grep continu-asse a ver a cadeia de caracteres resul-tante da expansão da linha pelo Shellcomo um único argumento de pesquisa.
$ muslist “album 2”
album2^Artista3~Musica3:Artista4U
~Musica4
Agora repare que o grep localiza acadeia pesquisada em qualquer lugar doregistro; então, da forma que estamosfazendo, podemos pesquisar por álbum,por música, por intérprete e mais.Quando conhecermos os comandoscondicionais, montaremos uma novaversão de muslist que permitirá especi-ficar por qual campo pesquisar.
Ah! Em um dia de verão você foi àpraia, esqueceu os CDs no carro, aquele“solzinho” de 40 graus empenou seudisco favorito e agora você precisa deuma ferramenta para removê-lo dobanco de dados? Não tem problema,vamos desenvolver um script chamadomusexc, para excluir estes CDs.
Antes de desenvolver o “bacalho”,quero te apresentar a uma opção bas-tante útil da família de comandos grep. Éa opção -v, que quando usada lista todosos registros da entrada, exceto o(s) local-izado(s) pelo comando. Exemplos:
$ grep -v “album 2” musicas
album1^Artista1~Musica1:Artista2U
~Musica2
album3^Artista5~Musica5:Artista6U
~Musica6
Conforme expliquei antes, o grep doexemplo listou todos os registros demusicas exceto o referente a album 2,porque atendia ao argumento do co-
$ cat muslist
#!/bin/bash
# Consulta CDs (versao 1)
#
grep $1 musicas
Listagem 3 - muslist
$ cat muslist
#!/bin/bash
# Consulta CDs (versao 2)
#
grep -i “$1” musicas
Listagem 4muslist melhorado
$ cat musexc
#!/bin/bash
# Exclui CDs (versao 1)
#
grep -v “$1” musicas > /tmp/mus$$
mv -f /tmp/mus$$ musicas
Listagem 5 - musexc
[1] http://www.revistadolinux.com.br/ed/003/ferramentas.php3
[2] http://www.revistadolinux.com.br/ed/007/ereg.php3
[3] http://www.aurelio.net/er/livro/
INFORMAÇÕES
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������85Outubro2004www.linuxmagazine.com.br
Garçon! traga dois chopes porfavorquehojeeuvou terquefalar muito. Primeiro quero
mostrar uns programinhas simplesdeusaremuitoúteis,comoocut,queé usado para cortar um determinadopedaço de um arquivo. A sintaxe ealgunsexemplosdeusopodemservis-tosnoQuadro1:
Como dá para ver, existem quatrosintaxesdistintas:naprimeira(-c1-5)especifiquei uma faixa, na segunda(-c -6) especifiquei todo o texto atéumaposição,naterceira(-c4-)tudodeumadeterminadaposiçãoemdianteenaquarta(-c1,3,5,7,9),sóasposiçõesdeterminadas. A última possibilidade(-c-3,5,8-)foisóparamostrarquepode-mosmisturartudo.
Mas não pense que acabou por aí!Como você deve ter percebido, estaformadecutémuitoútilparalidarcomarquivoscomcamposdetamanhofixo,masatualmenteoquemaisexistesãoarquivoscomcamposdetamanhovari-ável,ondecadacampoterminacomumdelimitador.Vamosdarumaolhadanoarquivomusicasquecomeçamosapre-pararnaúltimavezqueviemosaquinobotequim.VejaoQuadro2.
Então, recapitulando, o layoutdo arquivo é o seguinte: nome do
álbum^intérprete1~nomedamúsica1:...:
intérpreten~nomedamúsican,istoé,onomedoálbumseráseparadoporumcircunflexo(^)dorestodoregistro,queéformadopordiversosgruposcompos-tospelo intérpretedecadamúsicadoCDearespectivamúsicainterpretada.Estesgrupossãoseparadosentresipordois-pontos(:)eointérpreteserásepa-radodonomedamúsicaporumtil(~).
Então,parapegarosdadosreferentesatodasassegundasmúsicasdoarquivomusicas,devemosdigitar:
$ cut -f2 -d: musicas
Artista2~(usica2
Artista4~(usica4
Artista6~(usica5
Artista8~(usica8@10_L:
Ou seja, cortamos o segundo campoz(-fdefield,campoeminglês)delimi-tado (-d) por dois-pontos (:).Mas, sequisermossomenteosintérpretes,deve-mosdigitar:
$ cut -f2 -d: musicas | cut U
-f1 -d~
Artista2
Artista4
Artista6
Artista8
Paraentendermelhorisso,vamosanali-saraprimeiralinhademusicas:
$ head -1 musicas
album 1^Artista1~(usica1: U
Artista2~(usica2
Entãoobserveoquefoifeito:
album 1^Artista1~(usica1: U
Artista2~(usica2
Destaforma,noprimeirocutoprimeirocampo do delimitador (-d) dois-pon-tos(:)éalbum1^Artista1~Musica1eosegundo,queéoquenos interessa,éArtista2~Musica2. Vamos então ver oqueaconteceunosegundocut:
Artista2~(usica2
Agora,primeirocampododelimitador(-d)til(~),queéoquenosinteressa,éArtista2 e o segundo éMusica2. Seo raciocínio que fizemos para a pri-
Umchopinho,umaperitivoeopapocontinua.Destavezvamosaprender
algunscomandosdemanipulaçãodecadeiasdecaracteres,queserãomuito
úteisnahoradeincrementarnossa“CDteca”.PORJULIOCEZARNEVES
CursodeShellScript
PapodebotequimIII
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
86 Outubro2004 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
meiralinhaforaplicadoaorestantedoarquivo, chegaremos à resposta ante-riormentedada.Outrocomandomuitointeressanteéotrqueserveparasubs-tituir,comprimirouremovercaracteres.Suasintaxesegueoseguintepadrão:
tr [opções] cadeia1 [cadeia2]
O comando copia o texto da entradapadrão(stdin),trocaasocorrênciadoscaracteresde cadeia1 pelo seu corres-pondentenacadeia2outrocamúltiplasocorrênciasdos caracteresde cadeia1por somente um caracter, ou aindacaracteres da cadeia1. As principaisopçõesdocomandosãomostradasnaTabela1.
Primeirovejaumexemplobembobo:
$ echo bobo | tr o a
baba
Istoé,troqueitodasasocorrênciasdaletra o pela letra a. Suponha que emdeterminado ponto do meu script eupeça ao operador para digitar s ou n(sim ou não), e guardo sua respostanavariável$Resp.Ora,oconteúdode$Resp pode conter letras maiúsculasouminúsculas,edestaformaeuteriaquefazerdiversostestesparasabersearespostadadafoiS,s,Noun.Entãoomelhoréfazer:
$ Resp=$ echo $Resp | tr S) sn
e após este comando eu teria certezadequeoconteúdode$Resp seriaums ou umn. Se o meu arquivo ArqEntestátodoemletrasmaiúsculasedesejopassá-lasparaminúsculaseufaço:
$ tr A-Z a-z < ArqEnt > / tmp/$$
$ m -f /tmp/$$ ArqEnt
NotequenestecasouseianotaçãoA-ZparanãoescreverABCD…YZ.Outrotipodenotaçãoquepodeserusadasãoasescape sequences (comoeu traduzi-ria? Seqüências de escape? Meio semsentido,né?Masválá…)quetambémsãoreconhecidasporoutroscomandosetambémnalinguagemC,ecujosigni-ficadovocêveránaTabela2:
Deixaeu tecontarum“causo”:umalunoqueestavadanadocomigoresol-veucomplicarminhavidaecomores-
postaaumexercícioprático,valendonota, que passei ele me entregou umscript com todos os comandos sepa-rados por ponto-e-vírgula (lembre-sequeoponto-e-vírgulaserveparasepa-rardiversoscomandosemumamesmalinha). Vou dar um exemplo simplifi-cado,eidiota,deumscriptassim:
$ cat confuso
echo leia Programação Shell U
Linu do Julio Cezar )e es U
> li ro;cat li ro;p d;ls;rm U
-f li ro2>/de /null;cd ~
Euexecuteioprogramaeelefuncionou:
$ confuso
leia Programação Shell Linu U
do Julio Cezar )e es
/home/jne es/L(
confuso li ro muse c musicas
musinc muslist numeros
Masnotadeprovaécoisaséria(enotade dólar é mais ainda) então, paraentenderoqueoalunohavia feito, ochameieemsuafrentedigitei:
$ tr ”;” ”\n” < confuso
echo leia Programação Shell U
Linu do Julio Cezar )e es
p d
cd ~
ls -l
rm -f li o 2>/de /null
Ocaraficoumuitodesapontado,porqueemdois ou três segundos eudesfiz agozaçãoqueeleperdeuhorasparafazer.Maspresteatenção!SeeuestivesseemumamáquinaUnix,euteriadigitado:
$ tr ”;” ”\012” < confuso
Agoravejaadiferençaentreoresultadodeumcomandodateexecutadohojeeoutroexecutadoháduassemanas:
Sun Sep 19 14:59:54 2004
Sun Sep 5 10:12:33 2004
Notouoespaçoextraapóso“Sep”nasegunda linha? Para pegar a hora eudeveriadigitar:
$ date | cut -f 4 -d ’ ’
14:59:54
Asintaxedocuté:cut-cPosIni-PosFim
[arquivo],ondePosIniéaposiçãoinicial,e
PosFimaposiçãofinal.Vejaosexemplos:
$ cat numeros
1234567890
0987654321
1234554321
9876556789
$ cut -c1-5 numeros
12345
09876
12345
98765
$ cut -c-6 numeros
123456
098765
123455
987655
$ cut -c4- numeros
4567890
7654321
4554321
6556789
$ cut -c1,3,5,7,9 numeros
13579
08642
13542
97568
$ cut -c -3,5,8- numeros
1235890
0986321
1235321
9875789
Quadro1–Ocomandocut
$ cat musicas
album 1^Artista1~(usica1:U
Artista2~(usica2
album 2^Artista3~(usica3:U
Artista4~(usica4
album 3^Artista5~(usica5:U
Artista6~(usica5
album 4^Artista7~(usica7:U
Artista8~(usica8
Quadro2–Oarquivomusicas
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������87Outubro2004www.linuxmagazine.com.br
Mas há duas semanas ocorreria oseguinte:
$ date | cut -f 4 -d ’ ’
5
Isto porque existem 2 caracteres embrancoantesdo5(dia).Entãooidealseriatransformarosespaçosembrancoconsecutivos em somente um espaçoparapodertratarosdoisresultadosdocomandodatedamesmaforma,eissosefazassim:
$ date | tr -s ” ”
Sun Sep 5 10:12:33 2004
Eagoraeupossocortar:
$ date | tr -s ” ” | cut -f 4 U
-d ” ”
10:12:33
OlhasócomooShellestáquebrandoogalho.VejaoconteúdodeumarquivobaixadodeumamáquinaWindows:
$ cat - e ArqDoDOS.t t
Este arqui o^($
foi gerado pelo^($
DOS/Win e foi^($
bai ado por um^($
ftp mal feito.^($
Dica:aopção-vdocatmostraoscarac-teresdecontroleinvisíveis,comanota-ção^L,onde^éa teclaControleLéarespectivaletra.Aopção-emostraofinaldalinhacomoumcifrão($).
IstoocorreporquenoDOSofimdosregistros é indicado por um Carriage
Return (\r – Retorno de Carro, CR) eumLineFeed(\f–AvançodeLinha,ouLF).NoLinuxporémofinaldo regis-troéindicadosomentepeloLineFeed.Vamoslimparestearquivo:
$ tr -d ’\r’ < ArqDoDOS.t t > /tmp/$$
$ m -f /tmp/$$ ArqDoDOS.t t
Agoravamosveroqueaconteceu:
$ cat - e ArqDoDOS.t t
Este arqui o$
foi gerado pelo$
DOS/R in e foi$
bai ado por um$
ftp mal feito.$
Bemaopção-ddotrremovedoarquivotodasasocorrênciasdocaractereespe-cificado. Desta forma eu removi oscaracteres indesejados, salvei o textoemumarquivotemporárioeposterior-menterenomeei-oparaonomeoriginal.Umaobservação:emumsistemaUnixeudeveriadigitar:
$ tr -d ’\015’ < ArqDoDOS.U
t t > /tmp/$$
Umadica:oproblemacomostermina-dores de linha (CR/LF) só aconteceuporque a transferênciado arquivo foifeitanomodobinário (ou image), Seantesdatransmissãodoarquivotivessesidoestipuladaaopçãoasciidoftp,istonãoteriaocorrido.
–Olha,depoisdestadicatôcomeçandoagostardestetaldeshell,masaindatemmuitacoisaquenãoconsigofazer.
–Poisé,aindanãotefaleiquasenadasobre programação em shell, aindatem muita coisa para aprender, mascomoqueaprendeu,jádápararesol-vermuitosproblemas,desdequevocêadquirao“modoshelldepensar”.Vocêseriacapazdefazerumscriptquedigaquaispessoasestão“logadas”hámaisdeumdianoseuservidor?
–Claroquenão!Paraissoserianecessá-rioeuconheceroscomandoscondicio-naisquevocêaindanãomeexplicoucomo funcionam. - Deixa eu tentarmudarumpoucoasualógicaetrazê-laparao“modoshelldepensar”,masantesémelhortomarmosumchope.Agoraquejámolheiapalavra,vamos
resolveroproblemaquetepropus.Vejacomofuncionaocomandowho:
$ ho
jne es pts/U
1 Sep 18 13:40
rtorres pts/U
0 Sep 20 07:01
rlegaria pts/U
1 Sep 20 08:19
lcarlos pts/U
3 Sep 20 10:01
Evejatambémodate:
$ date
(on Sep 20 10:47:19 BRT 2004
Repare que o mês e o dia estão nomesmo formato em ambos os coman-dos.Ora,seemalgumregistrodowhoeunãoencontraradatadehoje,ésinalque o usuário está “logado” há maisdeumdia, jáqueelenãopode terse
“logado”amanhã…Entãovamosguar-daropedaçoque importadadatadehojeparadepoisprocurá-lanasaídadocomandowho:
$ Data=$ date | cut -f 2-3 U
-d’ ’
Euusei a construção$(...), paraprio-rizaraexecuçãodoscomandosantesdeatribuirasuasaídaàvariávelData.Vamosversefuncionou:
$ echo $Data
Sep 20
Beleza!Agora,oquetemosquefazeréprocurarnocomandowhoosregistrosquenãopossuemestadata.
–Ah! Eu acho que estou entendendo!Vocêfalouemprocuraremeocorreuocomandogrep,estoucerto?
–Certíssimo!Sóqueeutenhoqueusarogrepcomaquelaopçãoqueelesólistaos registrosnosquaiselenãoencon-trou a cadeia. Você se lembra queopçãoéessa?
–Claro,a-v…–Isso!Táficandobom!Vamosver:
$ ho | grep - ”$Data”
jne es pts/U
1 Sep 18 13:40
Seeuquisesseumpoucomaisdeperfu-mariaeufariaassim:
$ ho | grep - ”$Data” |U
cut -f1 -d ’ ’
jne es
Viu?Nãofoinecessáriousarcomandocondicional, até porque o nossocomandocondicional,ofamosoif,nãotesta condição, mas sim instruções.Masantesvejaisso:
Opção Significado
-s Comprimenocorrênciasde
cadeia1emapenasuma
-d Removeoscaracteresdecadeia1
Tabela1–Ocomandotr
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
88 Outubro2004 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
$ ls musicas
musicas
$ echo $?
0
$ ls ArqIne istente
ls: ArqIne istente: )o suchU
fi le or director
$ echo $?
1
$ ho | grep jne es
jne es pts/1 Sep 18 U
13:40 10.2.4.144
$ echo $?
0
$ ho | grep juliana
$ echo $?
1
Oqueéqueesse$?fazaí?Algocome-çadoporcifrão($)pareceserumavari-ável, certo? Sim é uma variável quecontémocódigoderetornodaúltimainstrução executada. Posso te garan-tirqueseestainstruçãofoibemsuce-dida,$?teráovalorzero,casocontrárioseuvalorserádiferentedezero.Oquenosso comando condicional (if) faz étestarestavariável.Entãovamosverasuasintaxe:
if cmd
then
cmd1
cmd2
cmdn
else
cmd3
cmd4
cmdm
fi
Ouseja,casocomandocmdtenhasidoexecutado comsucesso, os comandosdoblocodothen(cmd1,cmd2ecmdn)serão executados, caso contrário, oscomandos do bloco opcional do else(cmd3,cmd4ecmdm)serãoexecutados.Oblocodoiféterminandocomumfi.
Vamosvernapráticacomoissofun-ciona,usandoumscriptqueincluiusu-áriosnoarquivo/etc/passwd:
$ cat incusu
#!/bin/bash
# Versão 1
if grep ^$1 /etc/pass d
then
echo Usuario \’$1\’U
já e iste
else
if useradd $1
then
echo Usuário \’$1\’ U
incluído em /etc/pass d
else
echo ”Problemas no U
cadastramento. Você é root?”
fi
fi
Reparequeo ifestátestandodiretoocomandogrepeestaéasuafinalidade.Casooifsejabemsucedido,ouseja,ousuário (cujo nome está em $1) foiencontradoem/etc/passwd,oscoman-dosdoblocodothenserãoexecutados(nesteexemplo,apenasoecho).Casocontrário,asinstruçõesdoblocodoelseserãoexecutadas,quandoumnovo iftestaseocomandouseradd foiexecu-tadoacontento,criandooregistrodousuário em /etc/passwd, ou exibindoumamensagemdeerro,casocontrário.
Executar o programa e passe comoparâmetroumusuáriojácadastrado:
$ incusu jne es
jne es: :54002:1001:Julio )e es:U
/home/jne es:/bin/U
bash
Usuario ’jne es’ ja e iste
No exemplo dado, surgiu uma linhaindesejada,elaéa saídadocomandogrep. Para evitar que isso aconteça,devemosdesviarasaídapara/dev/null.Oprogramaficaassim:
$ cat incusu
#!/bin/bash
# Versão 2
if grep ^$1 /etc/pass d > U
/de /null
then
echo Usuario \’$1\’ já U
e iste
else
if useradd $1
then
echo Usuário \’$1\’ U
incluído em /etc/pass d
else
echo ”Problemas no U
cadastramento. Você é root?”
fi
fi
Vamostestá-locomoumusuárionormal:
$ incusu Ze)inguem
./incusu[6]: useradd: not found
Problemas no cadastramento. U
Você é root?
Aquelamensagemdeerronãodeveriaaparecer! Para evitar isso, devemosredirecionarasaídadeerro(stderr)docomandouseraddpara/dev/null.Aver-sãofinalficaassim:
$ cat incusu
#!/bin/bash
# Versão 3
if grep ^$1 /etc/pass d > U
/de /null
then
echo Usuario \’$1\’ já U
e iste
else
if useradd $1 2> /de /null
then
echo Usuário \’$1\’ U
incluído em /etc/pass d
else
echo ”Problemas no U
cadastramento. Você é root?”
fi
fi
Depoisdisso,vejamosocomportamentodoprograma,seexecutadopeloroot:
$ incusu botelho
Usuário ’botelho’ incluido em U
/etc/pass d
Enovamente:
$ incusu botelho
Usuário ’botelho’ já e iste
Seqüência Significado Octal
\t Tabulação \011
\n Novalinha
<ENTER>
\012
\v Tabulação
Vertical
\013
\f Nova
Página
\014
\r Inícioda
linha<^M>
\015
\\ Umabarra
invertida
\0134
Tabela2
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������89Outubro2004www.linuxmagazine.com.br
Lembraqueeufaleiqueao longodosnossos papos e chopes os nossos pro-gramas iriam se aprimorando? Entãovejamosagoracomopodemosmelhoraronossoprogramaparaincluirmúsicasna"CDTeca":
$ cat musinc
#!/bin/bash
# Cadastra CDs ersao 3
#
if grep ”^$1$” musicas > U
/de /null
then
echo Este álbum já está U
cadastrado
else
echo $1 >> musicas
sort musicas -o musicas
fi
Comovocêviu,éumapequenaevolu-çãoemrelaçãoàversãoanterior.Antesde incluirumregistro (quenaversãoanterior poderia ser duplicado), testa-mosseoregistrocomeça(^)etermina($) de forma idêntica ao parâmetroálbumpassado($1).Ocircunflexo(^)
noiníciodacadeiaecifrão($)nofim,servempara testar seoparâmetro (oálbumeseusdados)éexatamenteiguala algum registro já existente. Vamosexecutar nosso programa novamente,masdestavezpassamoscomoparâme-troumálbumjácadastrado,praveroqueacontece:
$ musinc ”album 4^Artista7~U
(usica7:Artista8~(usica8”
Este álbum já está cadastrado
Eagoraumnãocadastrado:
$ musinc ”album 5^Artista9~U
(usica9:Artista10~(usica10”
$ cat musicas
album 1^Artista1~(usica1:U
Artista2~(usica2
album 2^Artista3~(usica3:U
Artista4~(usica4
album 3^Artista5~(usica5:U
Artista6~(usica5
album 4^Artista7~(usica7:U
Artista8~(usica8
album 5^Artista9~(usica9:U
Artista10~(usica10
Como você viu, o programa melho-rouumpouquinho,masaindanãoestápronto.Àmedidaqueeu teensinaraprogramaremshell,nossaCDtecavaificarcadavezmelhor.
–Entendi tudo que você me explicou,masaindanão sei como fazerum ifpara testar condições, ou seja o usonormaldocomando.
–Para issoexisteocomando test,quetesta condições. O comando if testaocomando test.Como já faleimuito,precisodeunschopesparamolharapalavra. Vamos parar por aqui e napróxima vez te explico direitinho ousodotestedediversasoutrassinta-xesdoif.
–Falou! Acho bom mesmo porque eutambém já tôficandozonzoeassimtenhotempoparapraticaressemontedecoisasquevocêmefalouhoje.Parafixaroquevocêaprendeu,tente
fazerumscriptizinhoparainformarseum determinado usuário, cujo nomeserá passado como parâmetro, está
“logado”nosistemaounão.–AêChico! trazmaisdois chopespra
mimporfavor… ■
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
84 edição04 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
Eaícara,tentoufazeroexercícioque te pedi em nosso últimoencontro?
– Claro que sim! Em programação, sevocênão treinarnãoaprende.Vocêmepediuumscriptparainformarseumdeterminadousuário,cujonomeserápassadocomoparâmetroparaoscript,está“logado”(arghh!)ounão.Fizoseguinte:
$ cat logado
#!/bin/bash
# Pesquisa se um usuário está
# logado ou não
if ho | grep $1
then
echo $1 está logado
else
echo $1 não está no pedaço
fi
– Calma rapaz! Jáviquevocêchegoucheiodetesão.PrimeirovamospedirosnossoschopesdepraxeedepoisvamosaoShell.Chico,trazdoischo-pes,umsemcolarinho!
– Aaah!Agoraquejámolhamososnos-sosbicos,vamosdarumaolhadanosresultadosdoseuprograma:
$ logado jne es
jne es pts/0 Oct 18 U
12:02 10.2.4.144
jne es está logado
Realmente funcionou. Passei meunome de usuário como parâmetro eeledissequeeuestavalogado,porémele imprimiuumalinhaextra,queeunão pedi, que é a saída do comandowho. Para evitar que isso aconteça, ésó mandá-la para o buraco negro domundo UNIX, o /dev/null. Vejamosentãocomoficaria:
$ cat logado
#!/bin/bash
# Pesquisa se uma pessoa está
# logada ou não ersão 2
if ho | grep $1 > /de /null
then
echo $1 está logado
else
echo $1 não está no pedaço
fi
Agoravamosaostestes:
$ logado jne es
jne es está logado
$ logado chico
chico não está no pedaço
Ah,agorasim!Lembre-sedessapega-dinha:amaiorpartedoscomandostemumasaídapadrãoeumasaídadeerros(o grep é uma das poucas exceções:elenãoexibeumamensagemdeerroquandonãoachaumacadeiadecarac-teres)edevemosredirecioná-lasparaoburaconegroquandonecessário.
Bem,agoravamosmudardeassunto:na última vez que nos encontramosaquinobotequim,quandojáestávamosdegoelaseca,vocêmeperguntoucomosetestamcondições.Paraisso,usamosocomandotest
TestesTodos estamos acostumados a usar oif para testar condições, e estas sãosempre maior que, menor que, maior
ouiguala,menorouiguala,igualae
Ogarçonjáperdeuacontadascervejas,eoassuntonãoacaba.Desta
vezvamosaprenderatestarosmaisvariadostiposdecondições,
parapodermoscontrolaraexecuçãodenossoprogramadeacordo
comaentradafornecidapelousuário.PORJULIOCEZARNEVES
CursodeShellScript
PapodebotequimIV
Opção Verdadeirose
-earq arqexiste
-sarq arqexisteetemtamanhomaiorquezero
-farq arqexisteeéumarquivoregular
-darq arqexisteeéumdiretório
-rarq arqexisteecomdireitodeleitura
-warq arqexisteecomdireitodeescrita
-xarq arqexisteecomdireitodeexecução
Tabela1–Opçõesdotestparaarquivos
Da
ve
Ha
milto
n-w
ww
.sxc.h
u
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������85edição04www.linuxmagazine.com.br
diferentede.Para testarcondiçõesemShellScriptusamosocomandotest,sóqueeleémuitomaispoderosodoqueaquilo com que estamos acostuma-dos. Primeiramente, veja na Tabela 1as principais opções (existem muitasoutras)para testararquivosemdiscoenaTabela2asprincipaisopçõesparatestedecadeiasdecaracteres.
Pensaqueacabou?Enganoseu!Agoraéhoradealgomaisfamiliar,asfamosascomparações com valores numéricos.Veja a Tabela 3, e some às opções jáapresentadasosoperadoresdaTabela4.
Ufa! Como você viu, tem coisa prachuchu,eonossoifémuitomaispode-rosoqueodosoutros.Vamosveremunsexemploscomoissotudofunciona.Testamosaexistênciadeumdiretório:
if test -d lmb
then
cd lmb
else
mkdir lmb
cd lmb
fi
No exemplo, testei a existência dodiretóriolmb.Senãoexistisse(else),eleseriacriado.Jásei,vocêvaicriticaraminhalógicadizendoqueoscriptnãoestáotimizado.Eusei,masqueriaquevocê o entendesse assim, para entãopoderusaro“ponto-de-espantação”(!)comoumnegadordotest.Vejasó:
if test ! -d lmb
then
mkdir lmb
fi
cd lmb
Desta forma o diretório lmb seriacriado somente se ele aindanão exis-tisse,eestanegativadeve-seaopontodeexclamação(!)precedendoaopção-d.Aofimdaexecuçãodessefragmentodescript,comcertezaoprogramaesta-riadentrododiretóriolmb.Vamosverdoisexemplosparaentenderadiferençanacomparaçãoentrenúmeroseentrecadeiasdecaracteres.
cad1=1
cad2=01
if test $cad1 = $cad2
then
echo As ariá eis são iguais.
else
echo As ariá eis são diferentes.
fi
Executandoofragmentodeprogramaacima,teremoscomoresultado:
As ariá eis são diferentes.
Vamosagoraalterá-loumpoucoparaqueacomparaçãosejanumérica:
cad1=1
cad2=01
if test $cad1 -eq $cad2
then
echo As ariá eis são iguais.
else
echo As ariá eis são diferentes.
fi
Evamosexecutá-lonovamente:
As ariá eis são iguais.
Comovocêviu,nasduasexecuçõesobtive resultadosdiferentes,porquea
cadeiadecaracteres“01”é realmentediferentede“1”.Porém,acoisamudadefiguraquandoasvariáveissãotesta-dasnumericamente,jáqueonúmero1éigualaonúmero01.
Paramostrarousodosconectores-o(ou)e-a(e),vejaumexemplo“animal”,programadodiretonopromptdoBash.Me desculpem os zoólogos, mas eunãoentendonadadereino,filo,classe,ordem,família,gênero,espécieeoutrascoisasdotipo,destaformaoqueestouchamandodefamíliaoudegênerotemgrandechancedeestartotalecomple-tamenteincorreto:
$ Familia=felinae
$ Genero=gato
$ if test $Familia = canidea U
-a $Genero = lobo -o $Familia = U
felina -a $Genero = leão
> then
> echo Cuidado
> else
> echo Pode passar a mão
> fi
Pode passar a mão
Nesteexemplo,casooanimal fossedafamíliacanídeae(-a)dogênerolobo,ou(-o)dafamiliafelinae(-a)dogêneroleão,seriadadoumalerta,casocontrá-rioamensagemseriadeincentivo.
Atenção:Os sinaisdemaior (>)noiníciodas linhas internasaoifsãoosprompts de continuação (que estãodefinidosnavariável$PS2).Quandooshell identifica queum comando con-tinuarána linhaseguinte,automatica-menteelecolocaestecaractere,atéqueocomandosejaencerrado.
Vamosmudaroexemploparaverseoprogramacontinuafuncionando:
$ Familia=felino
$ Genero=gato
$ if test $Familia = felino -o U
$Familia = canideo -a $Genero = U
onça -o $Genero = lobo
> then
> echo Cuidado
> else
> echo Pode passar a mão
> fi
Cuidado
Obviamenteaoperaçãoresultouemerro,porqueaopção-atemprecedência
Opção Verdadeirose:
-zcadeia Tamanhodecadeiaézero
-ncadeia Tamanhodecadeiaémaiorquezero
cadeia Acadeiacadeiatemtamanhomaiorquezero
c1=c2 Cadeiac1ec2sãoidênticas
Tabela2–Opçõesdotestparacadeiasdecaracteres
Opção Verdadeirose Significado
n1-eqn2 n1en2sãoiguais equal
n1-nen2 n1en2nãosãoiguais notequal
n1-gtn2 n1émaiorquen2 greaterthan
n1-gen2 n1émaiorouigualan2 greaterorequal
n1-ltn2 n1émenorquen2 lessthan
n1-len2 n1émenorouigualan2 lessorequal
Tabela3–Opçõesdotestparanúmeros
Operador Finalidade
Parênteses() 0
Exclamação! 0
-a 0
-o 0
Tabela4
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
86 edição04 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
sobrea-oe,dessa,formaoquefoiava-liadoprimeirofoiaexpressão:
$Familia = canideo -a $Genero = U
onça
Que foi avaliada como falsa, retor-nandooseguinte:
$Familia = felino -o FALSO -o U
$Genero = lobo
Queresolvidaresultaem:
VERDADEIRO -o FALSO -o FALSO
Comoagoratodososconectoressão-o,eparaqueumasériedeexpressõesconectadas entre si por diversos “ou”lógicossejaverdadeira,bastaqueumadelasoseja.Aexpressãofinalresultoucomo VERDADEIRO e o then foi exe-cutadodeformaerrada.Paraqueissovolteafuncionarfaçamososeguinte:
$ if test \ $Familia = felino U
-o $Familia = canideo\ -a U
\ $Genero = onça -o $Genero = U
lobo\
> then
> echo Cuidado
> else
> echo Pode passar a mão
> fi
Pode passar a mão
Desta forma, com o uso dos parên-tesesagrupamosasexpressõescomoconector -o,priorizandoaexecuçãoeresultandoemVERDADEIRO-aFALSO.
Paraque sejaVERDADEIRO o resul-tado de duas expressões ligadas peloconector -a, é necessário que ambassejamverdadeiras,oquenãoéocasodoexemploacima.Assim,oresultadofinalfoiFALSO,sendoentãooelsecor-retamenteexecutado.
Se quisermos escolher um CD quetenhafaixasde2artistasdiferentes,nossentimostentadosausarum ifcomoconector-a,masésemprebomlembrarqueobashnosoferecemuitosrecursoseissopoderiaserfeitodeformamuitomaissimplescomumúnicocomandogrep,daseguinteforma:
$ grep Artista1 musicas | grep U
Artista2
Damesmaforma,paraescolhermosCDs que tenham a participação doArtista1 e do Artista2, não é necessá-riomontarumifcomoconector-o.Oegrep também resolve isso para nós.Vejacomo:
$ egrep Artista1|Artista2 U
musicas
Ou(nessecasoespecífico)oprópriogreppoderianosquebrarogalho:
$grep Artista[12] musicas
No egrep acima, foi usada umaexpressãoregular,naqualabarraver-tical(|)trabalhacomoum“oulógico”eosparênteses sãousadospara limitaraamplitudedeste“ou”.Jánogrepdalinhaseguinte,apalavraArtistadeveserseguidaporumdosvaloresdalistaformadapeloscolchetes([]),istoé,1ou2.
– Tá legal,euaceitooargumento,o ifdoshellémuitomaispoderosoqueosoutroscaretas-mas,cáentrenós,essaconstruçãodeiftest...émuitoesquisita,époucolegível.
– É, você tem razão, eu também nãogostodissoeachoqueninguémgosta.Acho que foi por isso que o shellincorporououtrasintaxe,quesubsti-tuiocomandotest.
Para issovamospegaraqueleexem-ploparafazerumatrocadediretórios,queeraassim:
if test ! -d lmb
then
mkdir lmb
fi
cd lmb
eutilizandoanovasintaxe,vamosfazê-loassim:
if [ ! -d lmb ]
then
mkdir lmb
fi
cd lmb
Ou seja, o comando test pode sersubstituídoporumpardecolchetes([]),separadosporespaçosembrancodosargumentos,oqueaumentaráenorme-
mentealegibilidade,poisocomandoifiráficarcomasintaxesemelhanteàdasoutraslinguagens;porisso,esseseráomodocomoocomandotestseráusadodaquiparaafrente.
Sevocêpensaqueacabou,estámuitoenganado.Presteatençãoà“TabelaVer-dade”naTabela5.
Ou seja, quandoo conector é e e aprimeiracondiçãoéverdadeira,oresul-tadofinalpodeserverdadeirooufalso,dependendodasegundacondição;jánoconectorou,casoaprimeiracondiçãosejaverdadeira,oresultadosempreseráverdadeiro.Seaprimeiraforfalsa,oresul-tadodependerádasegundacondição.
Ora, os caras que desenvolveram ointerpretador não são bobos e estãosempre tentandootimizaraomáximoos algoritmos. Portanto, no caso doconectore,asegundacondiçãonãoseráavaliada,casoaprimeirasejafalsa,jáque o resultado será sempre falso. Jácom o ou, a segunda será executadasomentecasoaprimeirasejafalsa.
Aproveitando-se disso, uma formaabreviadadefazertestesfoicriada.Oconectorefoibatizadode&&eooude||.Paravercomoissofunciona,vamosusá-los como teste no nosso velhoexemplodetrocadediretório,queemsuaúltimaversãoestavaassim:
if [ ! -d lmb ]
then
mkdir lmb
fi
cd lmb
Ocódigoacimatambémpoderiaserescritodemaneiraabreviada:
[ ! -d lmb ] && mkdir lmb
cd dir
Tambémpodemosretiraranegação(!):
[ -d lmb ] || mkdir lmb
cd dir
Combinação E OU
VERDADEIRO-VERDADEIRO TRUE TRUE
VERDADEIRO-FALSO FALSE TRUE
FALSO-VERDADEIRO FALSE TRUE
FALSO-FALSO FALSE FALSE
Tabela5-TabelaVerdade
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������87edição04www.linuxmagazine.com.br
No primeiro caso, se o primeirocomando (o test, que está represen-tadopeloscolchetes)forbemsucedido,istoé,seodiretóriolmbnãoexistir,ocomandomkdirseráexecutadoporqueaprimeiracondiçãoeraverdadeiraeoconectorerae.
Noexemploseguinte, testamosseodiretóriolmbexistia(noanteriortesta-mosseelenãoexistia)e,casoissofosseverdade,omkdirnãoseriaexecutadoporqueoconectoreraou.Outraformadeescreveroprograma:
cd lmb || mkdir lmb
Nesse caso, se o comando cd fossemal sucedido, o diretório lmb seriacriadomasnãoseriafeitaamudançade
diretórioparadentrodele.Paraexecu-tarmaisdeumcomandodessaforma,énecessário fazerumgrupamentodecomandos, o que se consegue com ousodechaves({}).Vejacomoseriaomodocorreto:
cd lmb ||
{
mkdir lmb
cd lmb
}
Aindanãoestá legalporque,casoodiretórionãoexista,ocdexibiráumamensagemdeerro.Vejaomodocerto:
cd lmb 2> /de /null ||
{
mkdir lmb
cd lmb
}
Comovocêviu,ocomandoifnosper-mitiu fazerum cd segurodediversasmaneiras.Ésemprebomlembrarqueo
“seguro”aquemerefirodizrespeitoaofatodequeaofinaldaexecuçãovocê
sempreestarádentrodelmb,desdequetenhapermissãoparaentrarnestedire-tório,permissãoparacriarumsubdire-tóriodentrode../lmb,quehajaespaçoemdiscosuficiente...
Vejamosumexemplodidático:depen-dendodovalordavariável$opcoscriptdeverá executar uma das opções aseguir:inclusão,exclusão,alteraçãoouencerrarsuaexecução.Vejacomofica-riaocódigo:
if [ $opc -eq 1 ]
then
inclusao
elif [ $opc -eq 2 ]
then
e clusao
elif [ $opc -eq 3 ]
then
alteracao
elif [ $opc -eq 4 ]
then
e it
else
echo Digite uma opção entre U
1 e 4
fi
Caractere Significado
* Qualquercaractereocorrendozeroou
maisvezes
? Qualquercaractereocorrendoumavez
[...] Listadecaracteres
| “ou”lógico
Tabela6
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
88 edição04 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
Neste exemplo você viu o uso docomando elif como um substituto ouformamaiscurtadeelseif.Essaéumasintaxeválidaeaceita,maspoderíamosfazeraindamelhor.Paraissousamosocomandocase,cujasintaxemostramosaseguir:
case $ ar in
padrao1 cmd1
cmd2
cmdn ;;
padrao2 cmd1
cmd2
cmdn ;;
padraon cmd1
cmd2
cmdn ;;
esac
Ondeavariável$varécomparadaaospadrõespadrao1,...,padraon.Casoumdospadrõescorrespondaàvariável,oblocodecomandoscmd1,...,cmdncor-respondenteéexecutadoatéencontrarumduploponto-e-vírgula(;;),quandoofluxodoprogramaseráinterrompidoe desviado para instrução imediata-menteapósocomandoesac(que,casonãotenhamnotado,écaseaocontrário.Eleindicaofimdoblocodecódigo,damesmaformaqueotcomandofiindicaofimdeumif).
Naformaçãodospadrões,sãoaceitososcaracteresmostradosnaTabela6.
Para mostrar como o código ficamelhor,vamosrepetiroexemploante-rior,sóquedestavezusaremosocaseemvezdotradicionalblocodecódigocomif...elif...else...fi.
case $opc in
1 inclusao ;;
2 e clusao ;;
3 alteracao ;;
4 e it ;;
* echo Digite uma opção U
entre 1 e 4
esac
Como você deve ter percebido, euuseioasteriscocomoúltimaopção,istoé,seoasteriscoatendeaqualquercoisa,entãoserviráparaqualquercoisaquenãoestejanointervalode1a4.Outracoisaasernotadaéqueoduploponto-e-vírgulanãoénecessárioantesdoesac.
Vamos agora fazer um script maisradical.Eletedarábomdia,boatardeouboanoite dependendodahora emqueforexecutado,masprimeiramentevejaestescomandos:
$ date
Tue )o 9 19:37:30 BRST 2004
$ date +%H
19
Ocomandodateinformaadatacom-pletadosistemaetemdiversasopçõesdemascaramentodo resultado.Nestecomando, a formatação começa comumsinaldemais(+)eoscaracteresdeformataçãovêmapósumsinaldeper-centagem(%),assimo%Hsignificaahoradosistema.Ditoisso,vejaoexem-plonoQuadro1.
Pegueipesado,né?Quenada,vamosesmiuçararesolução:
0?|1 [01]–Zeroseguidodequalquercoisa (?), ou (|) um seguido de zeroouum([01]),ouseja,estalinha"casa"com01,02,...09,10e11;1 [2-7]–Significaumseguidodalistadecaracteresentredoisesete,ouseja,estalinhapega12,13,...17;
*–Significatudooquenãocasoucomnenhumdospadrõesanteriores.
– Cara,atéagoraeufaleimuitoebebipouco. Agora eu vou te passar umexercícioparavocê fazeremcasaemedararespostadapróximavezemquenosencontrarmosaquinobote-quim,tálegal?
– Beleza!– Éoseguinte: façaumprogramaque
receba como parâmetro o nome deumarquivoequequandoexecutadosalveessearquivocomonomeorigi-nalseguidodeumtil(~)eabraessearquivodentrodoviparasereditado.Isso é para ter sempre uma cópiade backup do arquivo caso alguémfaça alterações indevidas. Obvia-mente, você fará as críticas neces-sárias,comoverificarsefoipassadoumparâmetro,seoarquivoindicadoexiste...Enfim,oquetedernatelhaevocêacharquedevaconstardoscript.Deupraentender?
– Hum,hum...– Chico,trazmaisum,semcolarinho!■
JulioCezarNeveséAnalistadeSuportedeSistemasdesde1969etrabalhacomUnixdesde1980,quandoparticipoudodesenvolvimentodoSOX,umsistemaoperacionalsimilaraoUnixpro-duzidopelaCobracomputadores.Podesercontatadonoe-mailjulio.neves@gmail.com
SO
BR
EO
AU
TO
R
#!/bin/bash
# Programa bem educado que
# dá bom-dia, boa-tarde ou
# boa-noite conforme a hora
Hora=$ date +%H
case $Hora in
0? | 1[01] echo Bom Dia
;;
1[2-7] echo Boa Tarde
;;
* echo Boa )oite
;;
esac
e it
Quadro1-Scriptbem-educado
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������89edição05www.linuxmagazine.com.br
Fala cara! E as idéias estão emordem?JáfundiuacucaouvocêaindaagüentamaisShell?
– Güento! Tô gostando muito! Gosteitantoqueatécapricheinoexercícioque você passou. Lembra que vocême pediu para fazer um programaquerecebecomoparâmetroonomedeumarquivoequequandoexecu-tadosalvaessearquivocomonomeoriginal seguido de um til (~) e oabredentrodovi?
– Claroquelembro,memostreeexpli-quecomovocêfez.
– Beleza,dáumaolhadanoquadro1– É,beleza!Masmedizumacoisa:por
quevocêterminouoprogramacomumexit0?
– Eu descobri que o número após oexit indica o código de retorno doprograma (o $?, lembra?) e assim,como a execução foi bem sucedida,ele encerra com o $?=0. Porém, sevocê observar, veráque casoopro-gramanãotenharecebidoonomedoarquivooucasoooperadornãotenhapermissãodegravaçãonessearquivo,o código de retorno ($?) seria dife-rentedozero.
– Grandegaroto,aprendeulegal,masébomdeixarclaroqueexit0,simples-menteexitounãocolocarexitprodu-zemigualmenteumcódigoderetorno($?) igualazero.Agoravamosfalarsobreas instruçõesde loopou laço,masantesvoupassaroconceitodeblocodecódigo.
Atéagorajávimosalgunsblocosdecódigo, como quando te mostrei umexemploparafazerumcdparadentrodeumdiretório:
cd lmb 2> /de /null ||
{
mkdir lmb
cd lmb
}
O fragmento contido entre as duaschaves({})formaumblocodecódigo.Tambémnesseexercícioqueacabamosdever,emquesalvamosoarquivoantesde editá-lo, existem vários blocos decódigocompreendidosentreoscoman-dosthenefidoif.Umblocodecódigotambémpodeestardentrodeumcaseouentreumdoeumdone.
– Peraí,Julio,quedoedonesãoesses?Não me lembro de você ter faladonisso, e olha que estou prestandomuitaatenção...
– Poisé,aindanãotinhafaladoporquenãohaviachegadoahoracerta.Todasas instruçõesde loopoulaço
executamoscomandosdoblococom-preendidosentreumdoeumdone.Asinstruçõesdeloopoulaçosãofor,whilee until , que serão explicadas uma aumaapartirdehoje.
OcomandoFor
Se você está habituado a programar,certamente já conheceocomando for,masoquevocênão sabe équeo for,
Blocosdecódigoelaços(ouloops,comopreferemalguns)
sãootemadomêsemmaisumaliçãodenossocursodeShell
Script.Garçom,saltaumaboaredondinha,quetôafimde
refrescaropensamento!PORJULIOCEZARNEVES
CursodeShellScript
PapodeBotequimV
$ cat ira.sh
#!/bin/bash
#
# ira - i resguardando
# arqui o anterior
# Verifi ca se algum parâmetro foi
# passado
if [ $# -ne 1 ]
then
echo Erro -> Uso: $0 U
<arqui o>
e it 1
fi
Arq=$1
# Caso o arqui o não e ista, não
# há cópia a ser sal a
if [ ! -f $Arq ]
then
i $Arq
e it 0
fi
# Se eu não puder alterar o
#arqui o, ou usar o i para que?
if [ ! - $Arq ]
then
echo Você não tem permissão U
de escrita em $Arq
e it 2
fi
# Já que está tudo OK, ou
# sal ar a cópia e chamar o i
cp -f $Arq $Arq~
i $Arq
e it 0
Quadro1:vira.sh
Da
ve
Ha
milto
n-w
ww
.sxc.h
u
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
90 edição05 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
queéumainstruçãointrínsecadoShell(isso significa que o código fonte docomandofazpartedocódigofontedoShell,ouseja,embomprogramêséumbuilt-in),émuitomaispoderosoqueosseuscorrelatosdasoutraslinguagens.
Vamos entender a sua sintaxe, pri-meiro em português e, depois, comofuncionapravaler.Olhesó:
para ar em al1 al2 ... aln
faça
cmd1
cmd2
cmdn
feito
Ondeavariávelvarassumecadaumdosvaloresdalistaval1val2...valne,paracadaumdessesvalores,executaoblocodecomandosformadoporcmd1,
cmd2 e cmdn. Agora que já vimos osignificadodainstruçãoemportuguês,vejamosasintaxecorreta:
for ar in al1 al2 ... aln
do
cmd1
cmd2
cmdn
done
Vamosaos exemplos,para entenderdireitoofuncionamentodestecomando.Vamos escrever um script para listartodososarquivosdodiretório,separa-dospordois-pontos,masantesvejaisso:
$ echo *
ArqDoDOS.t t1 confuso incusu
logado muse c musicas musinc
muslist
Isto é, o Shell viu o asterisco (*),expandiu-o com o nome de todos osarquivosdodiretórioeocomandoechojogou-osparaatelaseparadosporespa-çosembranco.Vistoisso,vamosresol-veroproblemaaquenospropusemos:
$ cat testefor1
#!/bin/bash
# 1o. Programa didático para
# entender o for
for Arq in *
do
echo -n $Arq:
done
Entãovamosexecutá-lo:
$ testefor1
ArqDoDOS.t t1:confuso:incusu:
logado:muse c:musicas:musinc:
muslist:$
Comovocêviu,oShelltransformouo asterisco (que odeia ser chamadode asterístico) em uma lista de arqui-vosseparadosporespaçosembranco.Quando o for viu aquela lista, disse:
“Opa, listas separadas por espaços écomigomesmo!”
Oblocodecomandosaserexecutadoerasomenteoecho,quecomaopção-nlistouavariável$Arqseguidadedois-pontos(:),semsaltaralinha.Ocifrão($)dofinalda linhadaexecuçãoéoprompt, que permaneceu na mesmalinhatambémemfunçãodaopção -n.Outroexemplosimples(porenquanto):
$ cat testefor2
#!/bin/bash
# 2o. Programa didático para
# entender o for
for Pala ra in Linu (agazine U
do Brasil
do
echo $Pala ra
done
Eexecutandotemos:
$ testefor2
Linu
(agazine
do
Brasil
Como você viu, esse exemplo étão bobo e simples como o anterior,mas serve para mostrar o comporta-mentobásicodofor.Vejasóaforçadocomando: ainda estamos na primeirapossibilidadedesintaxeejáestoumos-trandonovasformasdeusá-lo.Láatráseuhavia faladoqueo forusava listasseparadasporespaçosembranco,masissoéumameia-verdade,sóparafacili-taracompreensão.Naverdade,aslistasnãosãoobrigatoriamenteseparadasporespaços.Masantesdeprosseguir,pre-cisotemostrarcomosecomportaumavariáveldosistemachamadadeIFS,ouInterFieldSeparatorVejanoexemploaseguirseuconteúdo:
$ echo $IFS | od -h
0000000 0920 0a0a
0000004
Istoé,mandeiavariável (protegidada interpretaçãodoShellpelasaspas)paraumdumphexadecimal(od-h).Oresultadopodeserinterpretadocomatabelaabaixo:
O último 0a foi proveniente do<ENTER>dadoaofinaldocomando.Paramelhoraraexplicação,vamosverissodeoutraforma:
$ echo :$IFS: | cat - et
: ^I$
:$
No comando cat, a opção -e repre-sentao<ENTER>comoumcifrão($)eaopção-trepresentao<TAB>comoum^I.Useiosdois-pontos(:)paramos-traro inícioeofimdoecho.Edessaforma,pudemosnotarqueostrêscarac-teresestãopresentesnaquelavariável.
Agoravejavocê:traduzindo,IFSsig-nificaseparadorentrecampos.Umavezentendidoisso,eupossoafirmarqueocomandofornãousaapenaslistassepa-radasporespaçosembranco,massimpelo conteúdo da variável $IFS, cujovalorpadrãosãooscaracteresqueaca-bamosdever.Paracomprovarmosisso,vamos continuar mexendo em nossaCDTeca, escrevendo um script querecebeonomedoartistacomoparâme-troelistaasmúsicasqueeletoca.Masprimeiramentevamosvercomoestáonossoarquivomusicas:
$ cat musicas
album 1^Artista1~(usica1:U
Artista2~(usica2
album 2^Artista3~(usica3:U
Artista4~(usica4
album 3^Artista5~(usica5:U
Artista6~(usica6
album 4^Artista7~(usica7:U
Artista1~(usica3
ValorHexadecimal Significado
09 <TAB>
20 <ESPAÇO>
0a <ENTER>
Tabela1:Resultadodood-h
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������91edição05www.linuxmagazine.com.br
album 5^Artista9~(usica9:U
Artista10~(usica10
Emcimadesse“leiaute”desenvolve-mososcriptaseguir:
$ cat listartista
#!/bin/bash
# Dado um artista, mostra as
# suas músicas
if [ $# -ne 1 ]
then
echo Você de eria ter U
passado um parâmetro
e it 1
fi
IFS=
:
for Art(us in $ cut -f2 -d^ U
musicas
do
echo $Art(us | grep $1 && U
echo $Art(us | cut -f2 -d~
done
O script, como sempre, começa tes-tandoseosparâmetrosforampassadoscorretamente,emseguidaoIFSfoiconfi-guradopara<ENTER>edois-pontos(:)(comodemonstramasaspasemlinhasdiferentes),porqueéelequemseparaosblocosArtistan~Musicam.Destaforma,avariável$ArtMusirárecebercadaumdessesblocosdoarquivo(reparequeoforjárecebeosregistrossemoálbumemvirtudedocutnasualinha).Casoencontreoparâmetro($1)nobloco,osegundocutlistarásomenteonomedamúsica.Vamosexecutaroprograma:
$ listartista Artista1
Artista1~(usica1
(usica1
Artista1~(usica3
(usica3
Artista10~(usica10
(usica10
Êpa!Aconteceramduascoisas inde-sejáveis:osblocostambémforamlista-dos,eaMusica10idem.Alémdomais,onossoarquivodemúsicasestámuitosimples: na vida real, tanto amúsicaquantooartistatêmmaisdeumnome.SuponhaqueoartistafosseumaduplasertanejachamadaPerereca&Peteleca(nãogostonemdedaraidéiacomreceioqueissosetornerealidade).Nessecaso,
o$1seriaPererecaeorestodesselindonomeseriaignoradonapesquisa.
Paraqueissonãoocorra,eudeveriapassaronomedoartistaentreaspas(”)outrocar$1por$*(querepresentatodososparâmetrospassados),queéamelhorsolução,masnessecasoeuteriaquemodificaracríticadosparâ-metros e o grep. A nova versão nãoseriaseeupasseiumparâmetro,massim se passei pelo menos um parâ-metro.Quantoaogrep,vejasóoqueaconteceriaapósasubstituiçãodo$*pelosparâmetros:
echo $Art(us | grep perereca U
& peteleca
Issogeraumerro.Ocorretoé:
echo $Art(us | grep -i U
perereca & peteleca
Aqui adicionamos a opção -i paraqueapesquisaignorassemaiúsculaseminúsculas.Asaspas foraminseridasparaqueonomedoartistafossevistocomoumasócadeiadecaracteres.
FaltaconsertaroerrodeleterlistadooArtista10.Omelhorédizeraogrepqueacadeiadecaracteresestánoinício(^)de$ArtMusequelogoapósvemumtil(~).Éprecisoredirecionarasaídadogreppara/dev/nullparaqueosblocosnãosejamlis-tados.Vejaanovacaradoprograma:
$ cat listartista
#!/bin/bash
# Dado um artista, mostra as
# suas musicas
# Versao 2
if [ $# -eq 0 ]
then
echo Voce de eria ter U
passado pelo menos um parametro
e it 1
fi
IFS=
:
for Art(us in $ cut -f2 -d^ U
musicas
do
echo $Art(us | grep -i U
^$*~ > /de /null && echo U
$Art(us | cut -f2 -d~
done
Oresultadoé:
$ listartista Artista1
(usica1
(usica3
Vejaumasegundasintaxeparaofor:
for ar
do
cmd1
cmd2
cmdn
done
Ué,semoin,comoelevaisaberquevalorassumir?Poisé,né?Estaconstru-ção,àprimeiravista,pareceesquisita,masébastantesimples.Nestecaso,varassumiráumaumcadaparâmetropas-sadoparaoprograma.Comoexemploparaentendermelhor,vamosfazerumscriptquerecebacomoparâmetroummontedemúsicaselisteseusautores:
$ cat listamusica
#!/bin/bash
# ”ecebe parte dos nomes de
# músicas como parâmetro e
# lista os intérpretes. Se o
# nome for composto, de e
# ser passado entre aspas.
# e . Eu não sou cachorro não
# Churrasquinho de (ãe
#
if [ $# -eq 0 ]
then
echo Uso: $0 musica1 U
[musica2] ... [musican]
e it 1
fi
IFS=
:
for (usica
do
echo $(usica
Str=$ grep -i $(usica U
musicas ||
{
echo )ão U
encontrada
continue
}
for Art(us in $ echo $Str U
| cut -f2 -d^
do
echo $Art(us | U
grep -i $(usica | cut -f1 -d~
done
done
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
92 edição05 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
Damesmaformaqueosoutros,come-çamosoexercíciocomumacríticasobreos parâmetros recebidos, em seguidafizemos um for em que a variável$Musicareceberácadaumdosparâme-trospassados,colocandoem$Strtodososálbunsquecontêmasmúsicasdese-jadas.Emseguida,ooutroforpegacadablocoArtista~Musicanosregistrosqueestão em$Str e lista cada artista quetocaaquelamúsica.Vamosexecutaroprogramaparaversefuncionamesmo:
$ listamusica musica3 (usica4 U
Egüinha Pocotó
musica3
Artista3
Artista1
(usica4
Artista4
Egüinha Pocotó
)ão encontrada
Alistagemficoufeinhaporqueaindanão sabemos formatar a saída; masqualquerdiadesses,quandovocêsou-berposicionarocursor,trabalharcomcoresetc.,faremosesseprogramanova-menteusandotodasessasperfumarias.
A esta altura dos acontecimentos,você deve estar se perguntando: “Eaquelefortradicionaldasoutraslingua-gensemqueelesaicontandoapartirdeumnúmero, comumdeterminadoincremento, até alcançar uma condi-ção?”.Eéaíqueeu te respondo:“Eunãotedissequeonossoforémaispor-retaqueodosoutros?”Parafazerisso,existemduas formas.Comaprimeirasintaxequevimos,comonoexemplo:
for i in $ seq 9
do
echo -n $i
done
1 2 3 4 5 6 7 8 9
Avariáveliassumiuosvaloresintei-rosentre1a9geradospelocomandoseqeaopção-ndoechofoiusadaparanãosaltarumalinhaacadanúmerolis-tado.Aindausandooforcomseq:
for i in $ seq 4 9
do
echo -n $i
done
4 5 6 7 8 9
Ounaformamaiscompletadoseq:
for i in $ seq 0 3 9
do
echo -n $i
done
0 3 6 9
A outra forma de fazer isso é comumasintaxemuitosemelhanteaofordalinguagemC,comovemosaseguir:
for ar=ini; cond; incr
do
cmd1
cmd2
cmdn
done
Onde var=ini significa que a variá-vel var começará de um valor inicialini;condsignificaqueoloopoulaçoforseráexecutadoenquantovarnãoatingiracondiçãocondeincrsignificaoincre-mentoqueavariávelvarsofreráacadapassadadoloop.Vamosaosexemplos:
for i=1; i<=9; i++
do
echo -n $i
done
1 2 3 4 5 6 7 8 9
Avariávelipartiudovalorinicial1,oblocodecódigo(aquisomenteoecho)seráexecutadoenquantoiformenorouigual(<=)a9eoincrementodeiseráde1acadapassadadoloop.
Reparequenoforpropriamentedito(enãonoblocodecódigo)nãocoloqueium cifrão ($) antes do i e a notaçãoparaincrementar(i++)édiferentedoquevimosatéagora.Ousodeparênte-sesduplos(assimcomoocomandolet)chama o interpretador aritmético doShell,queémaistolerante.
Sóparamostrarcomooletfuncionaeaversatilidadedofor,vamosfazeramesma coisa, mas omitindo a últimapartedoescopodofor,passando-aparaoblocodecódigo:
for ; i<=9;
do
let i++
echo -n $i
done
1 2 3 4 5 6 7 8 9
Repare que o incremento saiu docorpodoforepassouparaoblocodecódigo; repare também que, quandousei o let, não foi necessário iniciali-zaravariável$i.Vejasóoscomandosaseguir,digitadosdiretamentenoprompt,parademonstraroqueacabodefalar:
$ echo $j
$ let j++
$ echo $j
1
Ouseja,avariável$jsequerexistiaenoprimeiroletassumiuovalor0(zero)para,apósoincremento,terovalor1.Vejasócomoascoisasficamsimples:
for arq in *
do
let i++
echo $i -> $Arq
done
1 -> ArqDoDOS.t t1
2 -> confuso
3 -> incusu
4 -> listamusica
5 -> listartista
6 -> logado
7 -> muse c
8 -> musicas
9 -> musinc
10 -> muslist
11 -> testefor1
12 -> testefor2
– Poiséamigo,tenhocertezaquevocêjá tomou um xarope do comandofor.Porhojechega,napróximavezemquenosencontrarmosfalaremossobreoutrasinstruçõesdeloop,maseugostariaqueatélávocêfizesseumpequenoscriptparacontaraquanti-dadedepalavrasdeumarquivotexto,cujonomeseriarecebidocomoparâ-metro. Essa contagem tem que serfeitacomocomandofor,parasehabi-tuaraoseuuso.Nãovaleusarowc-w.AêChico!Trazasaideira! ■
JulioCezarNeveséAnalistade
SuportedeSistemasdesde1969etra-
balhacomUnixdesde1980,quando
participoudodesenvolvimentodo
SOX,umsistemaoperacionalsimilar
aoUnixproduzidopelaCobraCom-
putadores.Podesercontatadono
BR
EO
AU
TO
R
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
86 edição06 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
Fala,cara!Eaí,játásabendotudodocomandofor?Eutedeixeiumexercícioparatreinar,senãome
enganoeraparacontaraquantidadedepalavrasdeumarquivo...Vocêfez?– Claro! Tô empolgadão com essa lin-guagem! Eu fiz da forma que vocêpediu,olhasó...
– Êpa! Peraí que eu tô sequinho pratomarumchope.AêChico,trazdoisporfavor.Umsemcolarinho!
– Comoeuiadizendo,olhacomoeufiz.Émuitofácil...
$ cat contpal.sh#!/bin/bash# Script meramente pedagógico# cuja função é contar a# quantidade de pala ras de# um arqui o. Supõe-se que as# pala ras estão separadas# entre si por espaços, <TAB># ou <E)TER>.if [ $# -ne 1 ]then echo uso: $0 /caminho/do/Uarqui o e it 2fi Cont=0for Pala ra in $ cat $1do Cont=$ Cont+1doneecho O arqui o $1 tem $Cont Upala ras.
Ou seja, o programa começa, comosempre,verificandoseapassagemdeparâmetros foi correta; em seguida ocomandoforseincumbedepegarcadaumadaspalavras(lembre-sequeo$IFSpadrãoébranco,TABeENTER,queéexatamenteoquedesejamosparasepa-raraspalavras),incrementandoavari-ável$Cont.VamosrelembrarcomoéoarquivoArqDoDOS.txt.
$ cat ArqDoDOS.t tEste arqui ofoi gerado peloDOS/R in e foibai ado por umftp mal feito.
Agoravamos testaroprogramapas-sandoessearquivocomoparâmetro:
$ contpal.sh ArqDoDOS.t tO arqui o ArqDoDOS.t t tem 14pala ras.
Funcionou legal!Se você se lembra,em nossa últimaaula mostramos oloopforaseguir:
for ; i<=9;do let i++ echo -n "$i "done
Umavezquechegamosnesteponto,creioserinteressantecitarqueoShelltrabalhacomoconceitode“ExpansãoAritmética” (Arithmetic Expansion),queéacionadaporumaconstruçãodaforma$((expressão))ouletexpressão.Noúltimo loop forusei a expansãoaritmética das duas formas,mas nãopodemosseguiradiantesemsaberqueaexpressãopodeserumadaslistadasnatabela1.Masvocêpensaqueopapodeloop
(ou laço) se encerra no comando for?Ledoengano,amigo,vamosapartirdeagoravermaisdoiscomandos.
O��ma���while
Todososprogramadoresconhecemestecomando,porqueécomumatodasaslinguagens.Nelas,oquenormalmenteocorreéqueumblocodecomandoséexecutado, enquanto (enquanto, eminglês, é “while”) uma determinadacondiçãoforverdadeira.
Da
ve
Ha
milt�
�-w
ww
.sx�.h
u
Expressã� Resulta��
id++id-- pós-incrementoepós-decrementodevariáveis
++id--id pré-incrementoepré-decrementodevariáveis
** exponenciação
*/% multiplicação,divisão,restodadivisão(módulo)
+- adição,subtração
<=>=<> comparação
==!= igualdade,desigualdade
&& Elógico
|| OUlógico
Tabela1:Expressões��Shell
Blocosdecódigoelaços(ouloops,comopreferemalguns)
sãootemadomêsemmaisumaliçãodenossocursodeShell
Script.Garçom,saltaumaboaredondinha,quetôafimde
refrescaropensamento!PORJ�LIOCEZARNEVES
Curs��eShellS�ript
PapodeBotequimVI
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������87edição06www.linuxmagazine.com.br
Poisbem,issoéoqueacontecenaslinguagens caretas! Em programaçãoShell,oblocodecomandoséexecutadoenquantoumcomandoforverdadeiro.E é claro, sequiser testaruma condi-ção,useocomandowhilejuntocomocomando test, exatamente como vocêaprendeuafazernoif,lembra?Entãoasintaxedocomandoficaassim:
hile comandodo cmd1 cmd2 ... cmdndone
edessa forma,obloco formadopelasinstruçõescmd1,cmd2,...ecmdnéexe-cutadoenquantoaexecuçãodainstru-çãocomandoforbemsucedida.Suponha a seguinte cena: tinhauma tremenda gata me esperando eeuestavapresonotrabalhosempodersairporqueomeuchefe,queéumpénosaco(aliáschefe-chatoéumaredun-dância,né?),aindaestavanasaladele,queficabemnaminhapassagemparaarua.Elecomeçouaficarcabreirodepoisdaquintavezquepasseipelasuaportaeolheiparaversejáhaviaidoembora.Entãovolteiparaaminhamesaefiz,noservidor,umscriptassim:
$ cat logaute.sh#!/bin/bash# Espero que a Xu a não tenha# cop right de efe e ato :hile ho | grep efe
do sleep 30doneecho O ato se mandou, não Uhesite, dê e it e á à luta
Nestescriptzinho,ocomandowhiletestaopipelinecompostopeloscoman-doswho e grep, que será verdadeiroenquanto o grep localizar a palavraxefenasaídadocomandowho.Destaforma,oscriptdormirápor30segundosenquantoochefeestiverlogado(Argh!).Assimqueelesedesconectardoservi-dor,ofluxodoscriptsairádoloopetemostrará a tão ansiadamensagemdeliberdade.Masquandoexecuteioscript,adivinhaoqueaconteceu?
$ logaute.shefe pts/0 Jan 4 08:46 U10.2.4.144efe pts/0 Jan 4 08:46 U10.2.4.144
...efe pts/0 Jan 4 08:46 U10.2.4.144
Istoé,acada30segundosasaídadocomandogrepseriaenviadaparaatela,oquenãoélegal,jáquepoluiriaateladomeumicroeamensagemtãoespe-radapoderiapassardespercebida.Paraevitarisso,jásabemosqueasaídadopipelinetemqueserredirecionadaparaodispositivo/dev/null.
$ cat logaute.sh#!/bin/bash# Espero que a Xu a não tenha# cop right de efe e ato :hile ho | grep efe > /de /null
do sleep 30doneecho O ato se mandou, não Uhesite, dê e it e á a luta
Agora queromontar um script quereceba o nome (e eventuais parâme-tros)deumprogramaqueseráexecu-tadoembackgroundequemeinformedoseutérmino.Mas,paravocêenten-der este exemplo, primeiro tenho demostrarumanovavariáveldosistema.Vejaestescomandosexecutadosdireta-mentenoprompt:
$ sleep 10&[1] 16317$ echo $!16317[1]+ Done sleep 10$ echo $!16317
Isto é, criei um processo em back-ground que dorme por 10 segundos,somenteparamostrarqueavariável$!guardaoPID(ProcessID)doúltimopro-cesso embackground.Masobserve alistagemerepare,apósalinhadoDone,que a variável reteve o valor mesmoapósotérminodesseprocesso.Bem,sabendoisso,jáficamaisfácilmonitorarqualquerprocessoemback-ground.Vejasócomo:
$cat monbg.sh#!/bin/bash# E ecuta e monitora um# processo em background$1 & # Coloca em backgroudhile ps | grep -q $!
do sleep 5doneecho Fim do Processo $1
Essescriptébastantesimilaraoante-rior,mastemunsmacetesamais,vejasó:eletemqueserexecutadoemback-groundparanãoprenderopromptmaso$!seráodoprogramapassadocomoparâmetro, jáqueele foicolocadoembackground após omonbg.sh propria-mentedito.Reparetambémnaopção-q(quiet)dogrep,queserveparafazê-lo“trabalharemsilêncio”.Omesmoresul-tado poderia ser obtido com a linha:whileps|grep$!>/dev/null,comonosexemplosquevimosatéagora.Vamos melhorar o nosso velho
musinc,nossoprogramaparaincluirregistros no arquivomusicas, masantes preciso te ensinar a pegarumdadodatela,ejávouavisando:só vou dar uma pequena dica docomando read (que équempegaodadoda tela),quesejao suficientepara resolver este nosso problema.Emumaoutrarodadadechopevoute ensinar tudo sobre o assunto,inclusive como formatar tela, mashojeestamosfalandosobreloops.Asintaxe do comando read que nosinteressaporhojeéaseguinte:
$ read -p "prompt de leitura" ar
Onde “prompt de leitura” é o textoquevocêquerqueapareçaescritonatela.Quandoooperadorteclartaldado,ele será armazenado na variável var.Porexemplo:
$ read -p "Título do Álbum: " Tit
Bem,umavezentendidoisso,vamosà especificação do nosso problema:faremosumprogramaqueinicialmenteleráonomedoálbumeemseguidafaráumloopdeleitura,pegandoonomedamúsicaeoartista.Esse loop terminaquandoforinformadaumamúsicacomnomevazio,istoé,quandoooperador
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
88 edição06 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
derumsimples<ENTER>.Parafacili-taravidadooperador,vamosoferecercomodefaultomesmonomedoartistadamúsicaanterior(jáqueénormalqueoálbumsejatododomesmoartista)atéqueeledesejealterá-lo.Vejanalistagem1comoficouoprograma.Nosso exemplo começa com a lei-turadotítulodoálbum.Casoelenãoseja informado, terminamos a execu-çãodoprograma.Emseguidaumgrepprocura,noinício(^)decadaregistrodemúsicas,otítuloinformadoseguidodoseparador(^)(queestáprecedidodeumacontrabarra[\]paraprotegê-lodainterpretaçãodoShell).Para ler os nomes dos artistas e asmúsicasdoálbum,foimontadoumloopwhilesimples,cujoúnicodestaqueéofatodeelearmazenaronomedointér-prete da música anterior na variável$oArt,quesóteráoseuconteúdoalte-radoquandoalgumdadoforinformadoparaavariável$Art,istoé,quandonãofor teclado um simples ENTER paramanteroartistaanterior.Oquefoivistoatéagorasobreowhilefoimuitopouco.Essecomandoémuitoutilizado, principalmentepara leiturade arquivos, porém ainda nos faltabagagemparaprosseguir.Depoisqueaprendermosmaissobreisso,veremosessainstruçãomaisafundo.
O��ma���untilEste comando funciona de formaidênticaaowhile,porémaocontrário.Dissetudomasnãodissenada,né?Éo seguinte: ambos testam comandos;ambos possuem a mesma sintaxe eambosatuamemloop;porém,owhileexecutaoblocodeinstruçõesdoloopenquanto um comando for bem suce-dido;jáountilexecutaoblocodoloopatéqueocomandosejabemsucedido.Parecepoucacoisa,masadiferençaéfundamental.Asintaxedocomandoépraticamenteamesmadowhile.Veja:
until comandodo cmd1 cmd2 ... cmdndone
edessaformaoblocodecomandosfor-madopelasinstruçõescmd1,cmd2,...ecmdnéexecutadoatéqueaexecuçãodainstruçãocomandosejabemsucedida.Comoeutedisse,whileeuntilfuncio-namdeformaantagônica,eissoémuitofácil de demonstrar: em uma guerra,sempre que se inventa uma arma, oinimigobuscauma soluçãoparaneu-tralizá-la.Foibaseadonesseprincipiobelicosoquemeuchefedesenvolveu,nomesmoservidoremqueeuexecutavaologaute.sh,umscriptparacontrolaromeuhoráriodechegada.Umdiativemosumproblemanarede.Elemepediuparadarumaolhadanomicrodeleemedeixousozinhonasala.Resolvibisbilhotarosarquivos–guerraéguerra–evejasóoquedescobri:
$cat chegada.sh#!/bin/bashuntil ho | grep juliodo sleep 30doneecho $ date "+ Em %d/%m às U%H:%(h" > relapso.log
Olhaquesafado!Ocaraestavamon-tandoumlogcomosmeushoráriosdechegada, e ainda por cima chamouoarquivoderelapso.log!Oqueseráqueelequisdizercomisso?Nesse script, o pipelinewho | grep
julio, será bem sucedido somentequando julio for encontrado na saídado comandowho, isto é, quando eume “logar” no servidor. Até que issoaconteça,ocomandosleep,queformaoblocodeinstruçõesdountil,colocaráo programa em espera por 30 segun-dos.Quandoesseloopencerrar-se,seráenviadaumamensagemparaoarquivorelapso.log.Supondoquenodia20/01eume“loguei”às11:23horas,amensa-gemseriaaseguinte:
$ cat musinc.sh
#!/bin/bash
# Cadastra CDs ersao 4
#
clear
read -p "Título do Álbum: " Tit
[ "$Tit" ] || e it 1 # Fim da e ecução se título azio
if grep "^$Tit\^" musicas > /de /null
then
echo "Este álbum já está cadastrado"
e it 1
fi
Reg="$Tit^"
Cont=1
oArt=
hile true
do
echo "Dados da trilha $Cont:"
read -p "(úsica: " (us
[ "$(us" ] || break # Sai se azio
read -p "Artista: $oArt // " Art
[ "$Art" ] && oArt="$Art" # Se azio Art anterior
Reg="$Reg$oArt~$(us:" # (ontando registro
Cont=$ Cont + 1
# A linha anterior tb poderia ser Cont++
done
echo "$Reg" >> musicas
sort musicas -o musicas
Listagem1
Leituradearquivosignificalerumaum
todososregistros,oqueésempreuma
operaçãolenta.Fiqueatentoparanãousar
owhilequandofordesnecessário.OShell
temferramentascomoosedeafamília
grep,quevasculhamarquivosdeforma
otimizadasemquesejanecessárioousodo
whileparafazê-loregistroaregistro.
Di�a
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
LINUXUSERPapodeBotequim
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������89edição06www.linuxmagazine.com.br
Em 20/01 às 11:23h
Voltando à nossa CDteca, quandovamos cadastrar músicas seria idealque pudéssemos cadastrar diversosCDsdeumavezsó.Naúltimaversãodoprogramaissonãoocorre:acadaCDcadastradooprogramatermina.Vejanalistagem2comomelhorá-lo.Nestaversão,umloopmaiorfoiadi-cionadoantesdaleituradotítulo,quesó terminaráquandoavariável$Paradeixar de ser vazia. Caso o título doálbum não seja informado, a variá-vel$Parareceberáumvalor(coloquei1,mas poderia ter colocado qualquercoisa)parasairdesseloop,terminandooprograma.Noresto,oscriptéidênticoàversãoanterior.
Atalh�s��l��p
Nem sempre umciclo de programa,c o m p r e e n d i d oentre um do e umdone,saipelaportada frente.Emalgu-masoportunidades,temos que colocarum comando queaborte de formacont rolada esseloop. De maneirainversa, algumasvezes desejamosque o f luxo deexecução do programa volte antes dechegar ao done. Para isso, temos res-
pectivamenteoscomandosbreak(quejávimosrapidamentenosexemplosdocomandowhile)econtinue,quefuncio-namdaformamostradanafigura1.O que eu não havia dito anterior-menteéquenassuassintaxesgenéricaselesaparecemdaseguinteforma:
break [qtd loop]
etambém:
continue [qtd loop]
Onde qtd loop representa a quanti-dadedosloopsmaisinternossobreosquaisoscomandosirãoatuar.Seuvalorpordefaulté1.Duvido que você nunca tenha apa-gadoumarquivoe logoapósdeuumtabefe na testa se xingando porquenão devia tê-lo removido. Pois é, nadécimavezquefizestabesteira,crieium script para simular uma lixeira,istoé,quandomandoremoverum(ouvários)arquivo(s),oprograma“finge”quedeletou,masnodurooqueelefezfoimandá-lo(s)paraodiretório/tmp/LoginName_do_usuario. Chamei esseprogramadeerreemeenoarquivo/etc/
profile coloquei a seguinte linha, quecriaum“apelido”paraele:
alias rm=erreeme
Vejaoprogramanalistagem3.Comovocêpodever,amaiorpartedoscripté formada por pequenas críticas aosparâmetros informados, mas como oscriptpodeterrecebidodiversosarqui-vosaremover,acadaarquivoquenãose encaixa dentro do especificado há
$ cat musinc.sh
#!/bin/bash
# Cadastra CDs ersao 5
#
Para=
until [ "$Para" ]
do
clear
read -p "Título do Álbum: " Tit
if [ ! "$Tit" ] # Se titulo azio...
then
Para=1 # Liguei fl ag de saída
else
if grep "^$Tit\^" musicas > /de /null
then
echo "Este álbum já está cadastrado"
e it 1
fi
Reg="$Tit^"
Cont=1
oArt=
hile [ "$Tit" ]
do
echo Dados da trilha $Cont:
read -p "(úsica: " (us
[ "$(us" ] || break # Sai se azio
read -p "Artista: $oArt // " Art
[ "$Art" ] && oArt="$Art" # Se azio Art anterior
Reg="$Reg$oArt~$(us:" # (ontando registro
Cont=$ Cont + 1
# A linha anterior tb poderia ser Cont++
done
echo "$Reg" >> musicas
sort musicas -o musicas
fi
done
Listagem2
Figura1:Aestrutura��s��ma���sbreakecontinue,usa��spara���tr�-
lar�flux��eexe�uçã�eml��ps.
��
��������
��������
����
�s�����ma�������� �s�����ma�����������
����� �����
��
��������
��������
����
����� ��������
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
90 edição06 www.linuxmagazine.com.br
PapodeBotequimLINUXUSER
umcontinue,paraqueaseqüênciavolteparao loopdo forde formaareceberoutrosarquivos.QuandovocêestánoWindows(comperdãodamápalavra)etentaremoveraquelemontedelixocomnomesesqui-sitos comoHD04TG.TMP, se der erroemumdosarquivososoutrosnãosãoremovidos,nãoé?Então,ocontinuefoiusadoparaevitarqueuma improprie-dadedessasocorra,istoé,mesmoquedêerrona remoçãodeumarquivo,oprograma continuará removendo osoutrosqueforampassados.– Euachoqueaestaalturavocêdeveestar curioso para ver o programaquerestauraoarquivoremovido,nãoé?Poisentãoaívaivaiumdesafio:
faça-oemcasaeme tragaparadis-cutirmosnonossopróximoencontroaquinoboteco.
– Poxa,masnesseeuachoquevoudan-çar,poisnãoseinemcomocomeçar...
– Cara, este programa é como tudoo que se faz em Shell: extrema-mente fácil.Éparaser feitoem,nomáximo,10 linhas.Não seesqueçadequeoarquivoestásalvoem/tmp/
$LOGNAMEequesuaúltimalinhaéodiretórioemqueele residiaantesde ser “removido”. Tambémnão seesqueçadecriticarse foipassadoonomedoarquivoaserremovido.
– Éeuvoutentar,masseinão...– Tenhafé,irmão,eutôtefalandoqueémole!Qualquerdúvidaésópassar
um email para [email protected]áestoudegoelasecadetantofalar.Meacompanhanopróximochopeoujávaisaircorrendoparafazeroscriptquepassei?
– Deixaeupensarumpouco...– Chico,trazmaisumchopeenquantoelepensa!
$ cat erreeme.sh
#!/bin/bash
#
# Sal ando cópia de um arqui o antes de remo ê-lo
# Tem de ter um ou mais arqui os a remo er
if [ $# -eq 0 ]
then
echo "Erro -> Uso: erreeme arq [arq] ... [arq]"
echo "O uso de metacaracteres e’ permitido. E .U
erreeme arq*"
e it 1
fi
# Variá el do sistema que contém o nome do usuário.
(euDir="/tmp/$LOG)A(E"
# Se não e istir o meu diretório sob o /tmp...
if [ ! -d $(euDir ]
then
mkdir $(euDir # Vou criá-lo
fi
# Se não posso gra ar no diretório...
if [ ! - $(euDir ]
then
echo "Impossi el sal ar arqui os em $(euDir. U
(ude as permissões..."
e it 2
fi
# Variá el que indica o cod. de retorno do programa
Erro=0
# Um for sem o in recebe os parametros passados
for Arq
do
# Se este arqui o não e istir...
if [ ! -f $Arq ]
then
echo "$Arq nao e iste."
Erro=3
continue # Volta para o comando for
fi
# Cmd. dirname informa nome do dir de $Arq
DirOrig=`dirname $Arq`
# Verifi ca permissão de gra acao no diretório
if [ ! - $DirOrig ]
then
echo "Sem permissão no diretorio de $Arq"
Erro=4
continue # Volta para o comando for
fi
# Se estou "es aziando a li eira"...
if [ "$DirOrig" = "$(euDir" ]
then
echo "$Arq fi cara sem copia de seguranca"
rm -i $Arq # Pergunta antes de remo er
# Será que o usuário remo eu?
[ -f $Arq ] || echo "$Arqui o remo ido"
continue
fi
# Guardo no fi m do arqui o o seu diretório originalU
para usá-lo em um script de undelete
cd $DirOrig
p d >> $Arq
m $Arq $(euDir # Sal o e remo o
echo "$Arq remo ido"
done
# Passo e entual número do erro para o código
# de retorno
e it $Erro
Listagem3:erreeme.sh
JulioCezarNeveséAnalistade
SuportedeSistemasdesde1969etra-
balhacomUnixdesde1980,quando
participoudodesenvolvimentodo
SOX,umsistemaoperacionalsimilar
aoUnixproduzidopelaCobraCom-
putadores.Podesercontatadono
BR
EO
AU
TO
R
Da
ve
Ha
milt
on
- ww
w.s
xc
.hu
De pouco adianta ter acesso à informação se ela não puder ser apresentada
de forma atraente e que facilite a compreensão. O comando tput pode ser
usado por shell scripts para posicionar caracteres e criar todos os tipos de
efeito com o texto mostrado na tela. Garçom, solta uma geladinha!
porJulioCezarNeves
Curso de Shell Script
PapodeBotequimParteVII
Cumequié,rapaz!Derreteuospen-samentosparafazeroscriptzinhoqueeutepedi?
–É,eurealmentetivedecolocarmuitapensaçãonatelapreta,masachoquefinalmenteconsegui!Bem,pelomenosnostestesquefizacoisafuncionou,masvocêtemsemprequebotarchifresemcabeçadecachorro!
–Nãoébemassim.ÉqueprogramaremShellScriptémuitofácil,masoqueérealmenteimportantesãoasdicasemacetesquenãosãotriviais.Ascor-reçõesquefaçosãojustamenteparamostrá-los.Masvamospedirdoischo-pesenquantodouumaolhadelanoteuscriptlána listagem 1.AêChico,trazdoischopes!Enãoseesqueçaqueumdelesésemcolarinho!
–Peraí,deixaeuverseentendioquevocêfez:vocêcolocanavariávelDiraúltimalinhadoarquivoaserrestaurado,emnossocaso/tmp/$LOG)A(E/$1(onde$LOG)A(Eéonomedousuáriologado,e$1éoprimeiroparâmetroquevocêpassouaoscript),jáquefoiláquearma-zenamosonomeecaminhooriginaisdoarquivoantesdemovê-loparaodiretório(definidonavariávelDir).Ocomandogrep - apagaessalinha,restaurandooarquivoaoestadooriginal,eomandadevoltapraondeeleveio.Aúltimalinhaoapagada“lixeira”.Sensacional!Impe-cável!Nenhumerro!Viu?Vocêjáestápegandoasmanhasdoshell!
–Entãovamoslá,chegadelesco-lescoeblá-blá-blá,sobreoquênósvamosfalarhoje?
–É,tôvendoqueobichinhodoshelltepegou.Vamosvercomolerdados,masantesvoutemostrarumcomandoquetedátodasasferramentasparaformatarumateladeentradadedados.
O comando tputOprincipalusodessecomandoéoposi-cionamentodocursornatela.Algunsparâmetrospodemnãofuncionarseomodelodeterminaldefinidopelavari-áveldeambiente$TER(nãosuportá-los.Atabela 1apresentaapenasosprincipaisparâmetroseosefeitosresultantes,masexistemmuitomaisdeles.Parasabertudosobreotput,vejaareferência[1].
Vamosfazerumprogramabembestaefácilparailustrarmelhorousodessecomando.Éumaversãodofamigerado
“AlôMundo”,sóquedessavezafraseseráescritanocentrodatelaeemvídeoreverso.Depoisdisso,ocursorvoltaráparaaposiçãooriginal.Vejaalistagem 2.
Comooprogramajáestátodocomen-tado,achoqueaúnicalinhaqueprecisadeexplicaçãoéa8,ondecriamosavariávelColuna.Oestranhoaliéaquelenúmero9,quenaverdadeindicaotamanhodacadeiadecaracteresquevouescrevernatela.Dessaforma,esteprogramasomenteconseguiriacentralizarcadeiasde9carac-teres,masvejaisto:
$ ar=Papo
$ echo ${# ar}
4
$ ar="Papo de Botequim"
$ echo ${# ar}
16
Listagem 1 – restaura.sh
01 #!/bin/bash
02 #
03 # Restaura arqui os deletados ia erreeme
04 #
05
06 if [ $# -eq 0 ]
07 then
08 echo "Uso: $0 <)ome do arqui o a ser restaurado>"
09 e it 1
10 fi
11 # Pega nome do arqui o/diret rio original na última linha
12 Dir='tail -1 /tmp/$LOG)A(E/$1'
13 # O grep - e clui a última linha e recria o arqui o com o diret rio
14 # e nome originalmente usados
15 grep - $Dir /tmp/$LOG)A(E/$1 > $Dir/$1
16 # Remo e o arqui o que já esta a moribundo
17 rm /tmp/$LOG)A(E/$1
LinuxUser
86
Papodebotequim
www.linuxmagazine.com.br
abril2005 edição07
Ahhh,melhorou!Entãoagorasabemosqueaconstrução${# aria el}devolveaquantidadedecaracteresdavariável.Assimsendo,vamosotimizaronossoprogramaparaqueeleescrevaemvídeoreverso,nocentrodatela(eindepen-dentedonúmerodecaracteres)acadeiadecaracterespassadacomoparâmetroedepoisretorneocursoràposiçãoemqueestavaantesdaexecuçãodoscript.Vejaoresultadonalistagem 3.
Estescriptéigualaoanterior,sóquetrocamosovalorfixonavariávelColuna(9)por${#1},ondeesse1é$1,ouseja,
essaconstruçãodevolveonúmerodecaracteresdoprimeiroparâmetropas-sadoparaoprograma.Seoparâmetrotivesseespaçosembranco,seriaprecisocolocá-loentreaspas,senãoo$1leva-riaemcontasomenteopedaçoantesdoprimeiroespaço.Paraevitaresteaborrecimento,ésósubstituiro$1por$*,quecomosabemoséoconjuntodetodososparâmetros.Entãoalinha8ficariaassim:
# Centralizando a mensagem na tela
Coluna=`$ Colunas - ${#*} / 2 `
ealinha12(echo $1)passariaaser:
echo $*
Lendo dados da telaBem,apartirdeagoravamosaprendertudosobreleitura.Sónãopossoensinaralercartasebúziosporquesesoubesseestariarico,numpubLondrinotomandoumscotchenãoemumbotecotomandochope.Masvamosemfrente.
Daúltimavezemquenosencontramoseudeiumapalhinhasobreocomandoread.Antesdeentrarmosemdetalhes,vejasóisso:
$ read ar1 ar2 ar3
Papo de Botequim
$ echo $ ar1
Papo
$ echo $ ar2
de
$ echo $ ar3
Botequim
$ read ar1 ar2
Papo de Botequim
$ echo $ ar1
Papo
$ echo $ ar2
de Botequim
Comovocêviu,oreadrecebeumalistadeparâmetrosseparadaporespa-çosembrancoecolocacadaitemdessalistaemumavariável.Seaquantidadedevariáveisformenorqueaquantidadedeitens,aúltimavariávelrecebeores-tantedeles.Eudisselistaseparadaporespaçosembranco,masagoraquevocêjáconhecetudosobreo$IFS(InterField
Separator–Separadorentrecampos),que
Tabela 1: Parâmetros do tput
Parâmetro Efeito
cup lin col CUrsor Position – Posiciona o cursor na linha lin e coluna col. A origem (0,0) fica no canto superior esquerdo da tela.
bold Coloca a tela em modo negrito
re Coloca a tela em modo de vídeo reverso
smso Idêntico ao anterior
smul Sublinha os caracteres
blink Deixa os caracteres piscando
sgr0 Restaura a tela a seu modo normal
reset Limpa o terminal e restaura suas definições de acordo com terminfo, ou seja, o ter-minal volta ao comportamento padrão definido pela variável de ambiente $TER(
lines Informa a quantidade de linhas que compõem a tela
cols Informa a quantidade de colunas que compõem a tela
el Erase Line – Apaga a linha a partir da posição do cursor
ed Erase Display – Apaga a tela a partir da posição do cursor
il n Insert Lines – Insere n linhas a partir da posição do cursor
dl n Delete Lines – Remove n linhas a partir da posição do cursor
dch n Delete CHaracters – Apaga n caracteres a partir da posição do cursor
sc Save Cursor position – Salva a posição do cursor
rc Restore Cursor position – Coloca o cursor na posição marcada pelo último sc
Listagem 2: alo.sh
01 #!/bin/bash
02 # Script bobo para testar
03 # o comando tput ersao 1
04
05 Colunas=`tput cols` # Sal a a quantidade de colunas na tela
06 Linhas=`tput lines` # Sal a a quantidade linhas na tela
07 Linha=$ Linhas / 2 # Qual é a linha central da tela?
08 Coluna=$ Colunas - 9 / 2 # Centraliza a mensagem na tela
09 tput sc # Sal a a posição do cursor
10 tput cup $Linha $Coluna # Posiciona o cursor antes de escre er
11 tput re # Vídeo re erso
12 echo Al (undo
13 tput sgr0 # Restaura o ídeo ao normal
14 tput rc # Restaura o cursor à posição original
Listagem 3: alo.sh melhorado
01 #!/bin/bash
02 # Script bobo para testar
03 # o comando tput ersão 2.0
04
05 Colunas=`tput cols` # Sal a a quantidade de colunas na tela
06 Linhas=`tput lines` # Sal a a quantidade de linhas na tela
07 Linha=$ Linhas / 2 # Qual é a linha central da tela?
08 Coluna=$ Colunas - ${#1} / 2 # Centraliza a mensagem na tela
09 tput sc # Sal a a posicao do cursor
10 tput cup $Linha $Coluna # Posiciona o cursor antes de escre er
11 tput re # Video re erso
12 echo $1
13 tput sgr0 # Restaura o ídeo ao normal
14 tput rc # De ol e o cursor à posição original
LinuxUser
88
Papodebotequim
www.linuxmagazine.com.br
abril2005 edição07
euteapresenteiquandofalávamosdocomandofor,seráqueaindaacreditanisso?Vamostestar:
$ oIFS="$IFS"
$ IFS=:
$ read ar1 ar2 ar3
Papo de Botequim
$ echo $ ar1
Papo de Botequim
$ echo $ ar2
$ echo $ ar3
$ read ar1 ar2 ar3
Papo:de:Botequim
$ echo $ ar1
Papo
$ echo $ ar2
de
$ echo $ ar3
Botequim
$ IFS="$oIFS"
Viu?euestavafurado!Oreadlêumalista,assimcomoofor,separadapeloscaracteresdavariável$IFS.Vejacomoissopodefacilitarasuavida:
$ grep julio /etc/pass d
julio: :500:544:Julio C. )e es - 7070:U
/home/julio:/bin/bash
$ oIFS="$IFS" # Sal a o IFS antigo.
$ IFS=:
$ grep julio /etc/pass d | read lname U
li o uid gid coment home shell
$ echo -e "$lname\n$uid\n$gid\n$comentU
\n$home\n$shell"
julio
500
544
Julio C. )e es - 7070
/home/julio
/bin/bash
$ IFS="$oIFS" # Restaura o IFS
Comovocêviu,asaídadogrepfoiredirecionadaparaocomandoread,queleutodososcamposdeumasótacada.Aopção-edoechofoiusada
paraqueo\nfosseentendidocomoumaquebradelinha(newline)enãocomoumliteral.SoboBashexis-temdiversasopçõesdoreadqueser-vemparafacilitarasuavida.Vejaatabela 2.
Eagoradiretoaosexemploscurtosparademonstrarestasopções.Paralerumcampo“Matrícula”:
# -n não salta linha
$ echo -n "(atricula: "; read (at
(atricula: 12345
$ echo $(at
12345
Podemossimplificarascoisasusandoaopção-p:
$ read -p "(atricula: " (at
(atricula: 12345
$ echo $(at
12345
Epodemoslerapenasumaquantidadepré-determinadadecaracteres:
$ read -n5 -p"CEP: " )um ; read -n3 U
-p- Compl
CEP: 12345-678$
$ echo $)um
12345
$ echo $Compl
678
Noexemploacimaexecutamosduasvezesocomandoread:umparaapri-meirapartedoCEPeoutraparaoseucomplemento,destemodoformatandoaentradadedados.Ocifrão($)logoapósoúltimoalgarismodigitadoénecessárioporqueoreadnãoincluipor padrão um caractere new lineimplícito,comooecho.
Paralersóduranteumdeterminadolimitedetempo(tambémconhecidocomotimeout):
$ read -t2 -p "Digite seu nome completo: U
" )om || echo 'Eita moleza!'
Digite seu nome completo: Eita moleza!
$ echo $)om
Oexemploacimafoiumabrincadeira,poiseusótinha2segundosparadigitaromeunomecompletoemaltivetempodeteclarumJ(aquelecoladonoEita),maseleserviuparamostrarduascoisas:P1)Ocomandoapósopardebarrasverti-
cais(oou–or–lógico,lembra-se?)seráexecutadocasoadigitaçãonãotenhasidoconcluídanotempoestipulado;
P2)Avariável)ompermaneceuvazia.ElasóreceberáumvalorquandooENTERforteclado.
$ read -sp “Senha: “
Senha: $ echo $REPLY
segredo :
Aproveiteiumerronoexemploante-riorparamostrarummacete.Quandoescreviaprimeiralinha,esquecidecolo-caronomedavariávelqueiriareceberasenhaesónoteiissoquandoiaescrevê-la.Felizmenteavariável$REPLYdoBashcontémaúltimaseqüênciadecaracteresdigitada–emeaproveiteidissoparanãoperderaviagem.Testevocêmesmooqueacabeidefazer.
Oexemploquedei,naverdade,eraparamostrarqueaopção-simpedequeoqueestásendodigitadosejamostradonatela.Comonoexemploanterior,afaltadonew
linefezcomqueopromptdecomando($)permanecessenamesmalinha.
Agoraquesabemoslerdatela,vejamoscomoselêemosdadosdosarquivos.
Lendo arquivosComoeujáhavialhedito,evocêdeveselembrar,o hiletestaumcomandoeexe-cutaumblocodeinstruçõesenquantoessecomandoforbemsucedido.Ora,quandovocêestálendoumarquivoparaoqualvocêtempermissãodeleitura,oreadsóserámalsucedidoquandoalcançaroEOF(EndOfFile–FimdoArquivo).Portanto,podemoslerumarquivodeduasmaneiras.Aprimeiraéredirecionandoaentradadoarquivoparaobloco hile,assim:
hile read Linha
do
echo $Linha
done < arqui o
Tabela 2: Opções do read
Opção Ação
-p prompt Escreve “prompt” na tela antes de fazer a leitura
-n num Lê até num caracteres
-t seg Espera seg segundos para que a leitura seja concluída
-s Não exibe na tela os caracteres digitados.
Papodebotequim LinuxUser
www.linuxmagazine.com.br
abril2005 edição07 89
Asegundaéredirecionandoasaídadeumcatparao hile,daseguintemaneira:
cat arqui o |
hile read Linha
do
echo $Linha
done
Cadaumdosprocessostemsuasvan-tagensedesvantagens.Oprimeiroémaisrápidoenãonecessitadeumsubshellparaassisti-lomas,emcontrapartida,oredirecionamentoficapoucovisívelemumblocodeinstruçõesgrande,oqueporvezesprejudicaavisualizaçãodocódigo.Osegundoprocessotrazavan-tagemdeque,comoonomedoarquivoestáantesdowhile,avisualizaçãodocódigoémaisfácil.Entretanto,oPipe(|)chamaumsubshellparainterpretá-lo,tornandooprocessomaislentoepesado.Parailustraroquefoidito,vejaosexem-plosaseguir:
$ cat readpipe.sh
#!/bin/bash
# readpipe.sh
# E emplo de read passando um arqui o
# por um pipe.
Ultimo=" azio "
# Passa o script $0 para o hile
cat $0 | hile read Linha
do
Ultimo="$Linha"
echo "-$Ultimo-"
done
echo "Acabou, Último=:$Ultimo:"
Vamosveroresultadodesuaexecução:
$ readpipe.sh
-#!/bin/bash-
-# readpipe.sh-
-# E emplo de read passando um arqui o
-# por um pipe.-
--
-Ultimo=" azio "-
-# Passa o script $0 para o hile-
-cat $0 | -
- hile read Linha-
-do-
-Ultimo="$Linha"-
-echo "-$Ultimo-"-
-done-
-echo "Acabou, Último=:$Ultimo:"-
Acabou, Último=: azio :
Comovocêviu,oscriptlistasuaspró-priaslinhascomumsinaldemenos(-)anteseoutrodepoisdecadaumae,nofinal,exibeoconteúdodavariável$Ultimo.Repare,noentanto,queoconteúdodessavariávelpermanecevazio.Ué,seráqueavariávelnãofoiatualizada?Foi,eissopodesercomprovadoporquealinhaecho "-$Ultimo-"listacorretamenteaslinhas.Entãoporqueissoaconteceu?
Comoeudisse,oblocodeinstruçõesredirecionadopelopipe(|)éexecutadoemumsubshelle,lá,asvariáveissãoatualizadas.Quandoessesubshellter-mina,asatualizaçõesdasvariáveisvãoparaasprofundezasdoinfernojuntocomele.Reparequevoufazerumapequenamudançanoscript,passandooarquivoporredirecionamentodeentrada(<),eascoisaspassarãoafuncionarnamaisperfeitaordem:
$ cat redirread.sh
#!/bin/bash
# redirread.sh
# E emplo de read passando o arqui o
# por um pipe.
Ultimo=" azio "
# Passa o script $0 para o hile
hile read Linha
do
Ultimo="$Linha"
echo "-$Ultimo-"
done < $0
echo "Acabou, Último=:$Ultimo:"
Vejacomoelerodaperfeitamente:
$ redirread.sh
-#!/bin/bash-
-# redirread.sh-
-# E emplo de read passando o arqui o
-# por um pipe.-
--
-Ultimo=" azio "-
- hile read Linha-
-do-
-Ultimo="$Linha"-
-echo "-$Ultimo-"-
-# Passa o script $0 para o hile-
-done < $0-
-echo "Acabou, Último=:$Ultimo:"-
Acabou, Último=:echo "Acabou,U
Último=:$Ultimo:":
Bem,amigosdaRedeShell,parafinali-zaraaulasobreocomandoreadsófaltamaisumpequenoeimportantemacete
quevoumostrarcomumexemploprático.Suponhaquevocêqueiralistarumarquivoequerqueacadadezregistrosessalistagempareparaqueooperadorpossalerocon-teúdodatela,equeelasócontinuedepoisdeooperadorpressionarqualquertecla.Paranãogastarpapel(daLinuxMagazine),voufazeressalistagemnahorizontal.Meuarquivo(numeros)tem30registroscomnúmerosseqüenciais.Veja:
$ seq 30 > numeros
$ cat 10porpag.sh
#!/bin/bash
# Programa de teste para escre er
# 10 linhas e parar para ler
# Versão 1
hile read )um
do
let ContLin++ # Contando...
# -n para não saltar linha
echo -n "$)um "
ContLin % 10 > /de /null || read
done < numeros
Natentativadefazerumprogramagenéricocriamosavariável$ContLin(navidareal,osregistrosnãosãosomentenúmerosseqüenciais)e,quandotestamosseorestodadivisãoerazero,mandamosasaídapara/de /null,praqueelanãoapareçanatela.Masquandofuiexecutaroprogramadescobriaseguintezebra:
$ 10porpag.sh
1 2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 U
18 19 20 21 23 24 25 26 27 28 29 30
Reparequefaltouonúmero11ealista-gemnãoparounoread.Todaaentradadoloopestavaredirecionadaparaoarquivonumerosealeiturafoifeitaemcimadessearquivo,perdendoonúmero11.Vamosmostrarcomodeveriaficarocódigoparaqueelepasseafuncionaracontento:
$ cat 10porpag.sh
#!/bin/bash
# Programa de teste para escre er
# 10 linhas e parar para ler - Versão 2
hile read )um
do
let ContLin++ # Contando...
# -n para não saltar linha
echo -n "$)um "
ContLin % 10 > /de /null || read U
< /de /tt
done < numeros
LinuxUser
90
Papodebotequim
www.linuxmagazine.com.br
abril2005 edição07
Reparequeagoraaentradadoreadfoiredirecionadapara/de /tt ,quenadamaisésenãooterminalcorrente,expli-citandodestaformaqueaquelaleituraseriafeitadotecladoenãodoarquivonumeros.Ébomrealçarqueissonãoacontecesomentequandousamosoredi-recionamentodeentrada;setivéssemosusadooredirecionamentoviapipe(|),omesmoteriaocorrido.Vejaagoraaexe-cuçãodoscript:
$ 10porpag.sh
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
Issoestáquasebom,masaindafaltaumpoucoparaficarexcelente.Vamosmelhoraroexemploparaquevocêoreproduzaeteste(mas,antesdetestar,aumenteonúmeroderegistrosemnume-rosoureduzaotamanhodatela,paraquehajaquebradelinha).
$ cat 10porpag.sh
#!/bin/bash
# Programa de teste para escre er
# 10 linhas e parar para ler
# Versão 3
clear
hile read )um
do
# Contando...
ContLin++
echo "$)um"
ContLin % `tput lines` - 3 ||
{
# para ler qualquer caractere
read -n1 -p"Tecle Algo " < /de /tt
# limpa a tela após a leitura
clear
}
done < numeros
Amudançasubstancialfeitanesteexem-ploécomrelaçãoàquebradepágina,jáqueelaéfeitaacadaquantidade-de-linhas-da-tela(tput lines)menos(-)três,istoé,seatelatem25linhas,oprogramalistará22registrosepararáparaleitura.Nocomandoreadtambémfoifeitaumaalteração,inseridooparâmetro-n1paralersomenteumcaracterequalquer,nãonecessariamenteumENTER,eaopção-pparaexibirumamensagem.
–Bemmeuamigo,porhojeésóporqueachoquevocêjáestádesacocheio…
–Numtônão,podecontinuar…–Sevocênãoestivereuestou…Masjá
quevocêestátãoempolgadocomoshell,voutedeixarumserviçobastantesim-plesparavocêmelhorarasuacdteca:Montetodaatelacomumúnicoechoedepoisposicioneocursoràfrentedecadacampoparareceberovalorqueserádigitadopelooperador.Nãoseesqueçamque,emcasode
qualquerdúvidaoufaltadecompanhiaparaumchopeésó[email protected]émparafazerumapropaganda:digamaosamigosquequemestiverafimdefazerumcurso“porreta”depro-gramaçãoemshelldevemandarume-mailparajulio.neves@tecnohall.com.brparainformar-se.Atémais!
Informações[1] Página oficial do Tput: http://www.cs.utah.edu/
dept/old/texinfo/tput/tput.html#SEC4
[2] Página oficial do Bash: http://www.gnu.org/ software/bash/bash.html
Papodebotequim LinuxUser
www.linuxmagazine.com.br
abril2005 edição07 91
Chegou a hora de fazer como Jack e dividir os programas
em pedacinhos. Com funções e chamadas externas
os scripts fi cam menores, a manutenção mais fácil
e ainda por cima reaproveitamos código.
porJúlioCezarNeves
–E aê,cara,tudobem?
–Tudobeleza!Euqueriatemostraroquefizmasjáseiquevocêvaiquerermolharobicoprimeiro,né?
–Sópratecontrariar,hojenãoquero.Vai,mostralogoaíoquevocêfez.
–Poxa,oexercícioquevocêpassouémuitogrande.Dáumaolhadanalistagem 1evêcomoeuresolvi:
–É,oprogramatálegal,tátodoestruturadinho,masgosta-riadefazeralgunspoucoscomentários:sópararelembrar,asseguintesconstruções:[ ! $Album ] &&e[ $(usica ] ||representamamesmacoisa,istoé:nocasodaprimeira,testamosseavariável$Albumnão(!)temnadadentro,então(&&)…Nasegunda,testamosse$(usicatemalgumdado,senão(||)…Sevocêreclamoudotamanhodoprograma,éporqueainda
nãotedeialgumasdicas.Reparequeamaiorpartedoscriptéparamostrarmensagenscentraliza-dasnapenúltimalinhadatela.RepareaindaquealgumasmensagenspedemumSouum)comorespostaeoutrassãosódeadvertência.Issoéumcasotípicoquepedeousodefunções,queseriamescritassomenteumavezeexecutadasemdiversospontosdoscript.Voumontarduasfunçõespararesolveressescasosevamosincor-porá-lasaoseuprogramaparaveroresultadofinal.–Chico!Agoratrazdoischopes,umsemcolarinho,paramedarinspira-ção.Evocê,deolhonalistagem 2.Comopodemosver,umafunçãoé
definidaquandodigitamosnome_da_função etodooseucorpoestáentrechaves({}).Jáconversamosaquinobotecosobrepassagemdeparâ-metroseasfunçõesosrecebemdamesmaforma,istoé,sãoparâmetros
posicionais($1, $2, …, $n).Todasasregrasqueseaplicamàpassagemdeparâmetrosparaprogramastambémvalemparafunções,masémuitoimportanterealçarqueosparâmetrospassadosparaumprogramanãoseconfundemcomaquelesquesãopassadosparasuasfunções.Issosignifica,porexem-plo,queo$1deumscriptédiferentedo$1deumadesuasfunçõesinternas.
Reparequeasvariáveis$(sg, $Tam(sge$Colsãodeusorestritodessarotinae,porisso,foramcriadascomovariáveislocais.Arazãoésimplesmenteaeconomiadememória,jáqueaosairdarotinaelasserãodevidamentedetonadas,coisaquenãoaconteceriaseeunãotivesseusadoesseartifício.
Alinhadecódigoquecriaavariávellocal(sgconcatenaaotextorecebido($1)umparêntese,arespostapadrão($2)emcaixaalta,umabarra,aoutraresposta($3)emcaixabaixaefinalizafechandooparêntese.Usoessaconvençãopara,ao
Curso de Shell Script
PapodeBotequimParteVIII
Listagem 1: musinc5.sh
01 $ cat musinc5.sh
02 #!/bin/bash
03 # Cadastra CDs ersao 5
04 #
05 clear
06 Linha(esg=$ `tput lines` - 3 # Linha onde serão mostradas as msgs para o operador
07 TotCols=$ tput cols # “td colunas da tela para enquadrar msgs
08 echo
Inclusão de (úsicas
======== == =======
Título do Álbum:
| Este campo foi
Fai a: < criado somente para
| orientar o preenchimento
)ome da (úsica:
Intérprete: # Tela montada com um único echo
09 hile true
10 do
11 tput cup 5 38; tput el # Posiciona e limpa linha ➟
Da
ve
Ha
milt
on
- ww
w.s
xc
.hu
LinuxUser
86
Papodebotequim
www.linuxmagazine.com.br
maio2005 edição08
mesmotempo,mostrarasopçõesdis-poníveiserealçararespostaoferecidacomopadrão.
Quasenofimdarotina,arespostarecebida($S))éconvertidaparacaixabaixa(minúsculas)deformaquenocorpodoprogramanãoprecisemosfazeresse teste.Vejana listagem 3comoficariaafunçãoparaexibirumamensagemnatela.
Essaéumaoutraformadedefinirumafunção:nãoachamamos,comonoexemploanterior,usandoumaconstruçãocomasintaxenome_da_função ,massimcomofunction nome_da_função.Emnadamaiseladiferedaanterior,excetoque,comoconstadoscomentários,usamosavariável$*que,comojásabemos,representaoconjuntodetodososparâmetrospassadosaoscript,paraqueoprogramadornãopreciseusaraspasenvolvendoamensagemquedesejapassaràfunção.
Paraterminarcomesseblá-blá-blá,vamosvernalistagem 4asalteraçõesnoprogramaquandousamosocon-ceitodefunções:
ReparequeaestruturadoscriptsegueaordemVariáveisGlobais,Fun-
çõeseCorpodoPrograma.Estaestrutu-raçãosedeveaofatodeShellScriptserumalinguageminterpretada,emqueoprogramaélidodaesquerdaparaadireitaedecimaparabaixo.Paraservistapeloscriptesuasfunções,umavariáveldeveserdeclarada(ouinicializada,comopreferemalguns)antesdequalqueroutracoisa.Porsuavez,asfunçõesdevemserdeclaradasantesdocorpodoprogramapropria-mentedito.Aexplicaçãoésimples:ointerpretadordecomandosdoshelldevesaberdoquesetrataafunçãoantesqueelasejachamadanopro-gramaprincipal.
Umacoisabacananacriaçãodefun-çõeséfazê-lastãogenéricasquantopossível,deformaquepossamserreutilizadasemoutrosscriptseapli-cativossemanecessidadedereinven-tarmosaroda.Asduasfunçõesqueacabamosdeversãobonsexemplos,poisédifícilumscriptdeentradadedadosquenãouseumarotinacomoa(anda(sgouquenãointerajacomooperadorpormeiodealgoseme-lhanteàPergunta.
12 read Album
13 [ ! $Album ] && # Operador deu <E)TE”>
14 {
15 (sg= Deseja Terminar? S/n
16 Tam(sg=${#(sg}
17 Col=$ TotCols - Tam(sg / 2 # Centraliza msg na linha
18 tput cup $Linha(esg $Col
19 echo $(sg
20 tput cup $Linha(esg $ Col + Tam(sg + 1
21 read -n1 S)
22 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
23 [ $S) = ) -o $S) = n ] && continue # $S) é igual a ) ou -o n?
24 clear; e it # Fim da e ecução
25 }
26 grep ^$Album\^ musicas > /de /null &&
27 {
28 (sg= Este álbum já está cadastrado
29 Tam(sg=${#(sg}
30 Col=$ TotCols - Tam(sg / 2 # Centraliza msg na linha
31 tput cup $Linha(esg $Col
32 echo $(sg
33 read -n1
34 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
35 continue # Volta para ler outro álbum
36 }
37 ”eg= $Album^ # $”eg receberá os dados para gra ação
38 oArtista= # Variá el que guarda artista anterior
39 hile true
40 do
41 Fai a++
42 tput cup 7 38
43 echo $Fai a
44 tput cup 9 38 # Posiciona para ler música
45 read (usica
46 [ $(usica ] || # Se o operador ti er dado <E)TE”>...
47 {
48 (sg= Fim de Álbum? S/n
49 Tam(sg=${#(sg}
50 Col=$ TotCols - Tam(sg / 2 # Centraliza msg na linha
51 tput cup $Linha(esg $Col
52 echo $(sg
53 tput cup $Linha(esg $ Col + Tam(sg + 1
54 read -n1 S)
55 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
56 [ $S) = ) -o $S) = n ] && continue # $S) é igual a ) ou -o n?
57 break # Sai do loop para gra ar
58 }
59 tput cup 11 38 # Posiciona para ler Artista
60 [ $oArtista ] && echo -n $oArtista # Artista anterior é default
61 read Artista
62 [ $Artista ] && oArtista= $Artista
63 ”eg= $”eg$oArtista~$(usica: # (ontando registro
64 tput cup 9 38; tput el # Apaga (úsica da tela
65 tput cup 11 38; tput el # Apaga Artista da tela
66 done
67 echo $”eg >> musicas # Gra a registro no fim do arqui o
38 sort musicas -0 musicas # Classifica o arqui o
69 done
papodebotequim LinuxUser
www.linuxmagazine.com.br
maio2005 edição08 87
Conselhodeamigo:crieumarquivoeanexeaelecadafun-çãonovaquevocêcriar.Aofinaldealgumtempovocêteráumabelabibliotecadefunçõesquelhepouparámuitotempodeprogramação.
O comando sourceVejasevocênotaalgodediferentenasaídadolsaseguir:
$ ls -la .bash_profile
-r -r--r-- 1 Julio unkno n 4511 Mar 18 17:45 .bash_profile
Nãoolhearespostanão,volteaprestaratenção!Bem,jáquevocêestámesmosemsacodepensareprefereleraresposta,voutedarumadica:achoquevocêjásabequeo.bash_pro-fileéumdosscriptsquesãoautomaticamenteexecutadosquandovocêse“loga”(ARRGGHH!Odeioessetermo!)nosistema.Agoraolhenova-menteparaasaídadocomandolsemedigaoquehádediferentenela.
Comoeudisse,o.bash_profileéexe-cutadoduranteologon,masreparequeelenãotemnenhumapermissãodeexecução.Issoaconteceporquesevocêoexecutassecomoqualqueroutroscriptcareta,nofimdesuaexecuçãotodooambienteporelegeradomorreriajuntocomoshellsoboqualelefoiexecutado(vocêselembradequetodososscriptssãoexecutadosemsub-shells,né?).Poiséparacoisasassimqueexisteocomandosource,tambémconhecidopor“.”(ponto).Estecomandofazcomqueoscriptquelheforpassadocomoparâmetronãosejaexecutadoemumsub-shell.Masémelhorumexemploqueumaexplicaçãoem453palavras.Vejaoscriptzinhoaseguir:
$ cat script_bobo
cd ..
ls
Elesimplesmentedeveriairparaodire-tórioacimadodiretórioatual.Vamosexecutarunscomandosenvolvendooscript_boboeanalisarosresultados:
$ p d
/home/jne es
$ script_bobo
jne es juliana paula sil ie
$ p d
/home/jne es
Seeumandeielesubirumdiretório,porquenãosubiu?Opa,peraíquesubiusim!Osub-shellquefoicriadoparaexe-cutaroscripttantosubiuquelistouosdiretóriosdosquatrousuáriosabaixodo
diretório/home.Sóqueassimqueaexecuçãodoscripttermi-nou,osub-shellfoiparaobeleléue,comele,todooambientecriado.Agorapresteatençãonoexemploabaixoevejacomoacoisamudadefigura:
$ source script_bobo
jne es juliana paula sil ie
$ p d
/home
$ cd -
/home/jne es
$ . script_bobo
jne es juliana paula sil ie
$ p d
/home
Listagem 2: Função Pergunta
01 Pergunta
02 {
03 # A função recebe 3 parâmetros na seguinte ordem:
04 # $1 - (ensagem a ser mostrada na tela
05 # $2 - Valor a ser aceito com resposta padrão
06 # $3 - O outro alor aceito
07 # Supondo que $1=Aceita?, $2=s e $3=n, a linha a
08 # seguir colocaria em (sg o alor Aceita? S/n
09 local (sg= $1 `echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`
10 local Tam(sg=${#(sg}
11 local Col=$ TotCols - Tam(sg / 2 # Centra msg na linha
12 tput cup $Linha(esg $Col
13 echo $(sg
14 tput cup $Linha(esg $ Col + Tam(sg + 1
15 read -n1 S)
16 [ ! $S) ] && S)=$2 # Se azia coloca default em S)
17 echo $S) | tr A-Z a-z # A saída de S) será em minúscula
18 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
19 return # Sai da função
20 }
Listagem 3: Função MandaMsg
01 function (anda(sg
02 {
03 # A função recebe somente um parâmetro
04 # com a mensagem que se deseja e ibir.
05 # para não obrigar o programador a passar
06 # a msg entre aspas, usaremos $* todos
07 # os parâmetros, lembra? e não $1.
08 local (sg= $*
09 local Tam(sg=${#(sg}
10 local Col=$ TotCols - Tam(sg / 2 # Centra msg na linha
11 tput cup $Linha(esg $Col
12 echo $(sg
13 read -n1
14 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
15 return # Sai da função
16 }
LinuxUser
88
Papodebotequim
www.linuxmagazine.com.br
maio2005 edição08
Listagem 4: musinc6.sh
01 $ cat musinc6
02 #!/bin/bash
03 # Cadastra CDs ersao 6
04 #
05 # Área de ariá eis globais
06 # Linha onde as mensagens serão e ibidas
07 Linha(esg=$ `tput lines` - 3
08 # “uantidade de colunas na tela para enquadrar as mensagens
09 TotCols=$ tput cols
10 # Área de funções
11 Pergunta
12 {
13 # A função recebe 3 parâmetros na seguinte ordem:
14 # $1 - (ensagem a ser dada na tela
15 # $2 - Valor a ser aceito com resposta default
16 # $3 - O outro alor aceito
17 # Supondo que $1=Aceita?, $2=s e $3=n, a linha
18 # abai o colocaria em (sg o alor Aceita? S/n
19 local (sg= $1 `echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`
20 local Tam(sg=${#(sg}
21 # Centraliza a mensagem na linha
22 local Col=$ TotCols - Tam(sg / 2
23 tput cup $Linha(esg $Col
24 echo $(sg
25 tput cup $Linha(esg $ Col + Tam(sg + 1
26 read -n1 S)
27 [ ! $S) ] && S)=$2 # Se azia, coloca default em S)
28 echo $S) | tr A-Z a-z # A saída de S) será em minúsculas
29 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
30 return # Sai da função
31 }
32 function (anda(sg
33 {
34 # A função recebe somente um parâmetro
35 # com a mensagem que se deseja e ibir;
36 # para não obrigar o programador a passar
37 # a msg entre aspas, usaremos $* todos
38 # os parâmetro, lembra? e não $1.
39 local (sg= $*
40 local Tam(sg=${#(sg}
41 # Centraliza mensagem na linha
42 local Col=$ TotCols - Tam(sg / 2
43 tput cup $Linha(esg $Col
44 echo $(sg
45 read -n1
46 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
47 return # Sai da função
48 }
49 # O corpo do programa propriamente dito começa aqui
50 clear
51 # A tela a seguir é montada com um único comando echo
52 echo
Inclusão de (úsicas
======== == =======
Título do Álbum:
Fai a:
)ome da (úsica:
Intérprete:
53 hile true
54 do
55 tput cup 5 38; tput el # Posiciona e limpa linha
56 read Album
57 [ ! $Album ] && # Operador deu <E)TE”>
58 {
59 Pergunta Deseja Terminar s n
60 # Agora só testo cai a bai a
61 [ $S) = n ] && continue
62 clear; e it # Fim da e ecução
63 }
64 grep -iq ^$Album\^ musicas 2> /de /null &&
65 {
66 (anda(sg Este álbum já está cadastrado
67 continue # Volta para ler outro álbum
68 }
69 ”eg= $Album^ # $”eg receberá os dados de gra ação
70 oArtista= # Guardará artista anterior
71 hile true
72 do
73 Fai a++
74 tput cup 7 38
75 echo $Fai a
76 tput cup 9 38 # Posiciona para ler música
77 read (usica
78 [ $(usica ] || # Se o operador teclou <E)TE”>...
79 {
80 Pergunta Fim de Álbum? s n
81 # Agora só testo a cai a bai a
82 [ $S) = n ] && continue
83 break # Sai do loop para gra ar dados
84 }
85 tput cup 11 38 # Posiciona para ler Artista
86 # O artista anterior é o padrão
87 [ $oArtista ] && echo -n $oArtista
88 read Artista
89 [ $Artista ] && oArtista= $Artista
90 ”eg= $”eg$oArtista~$(usica: # (ontando registro
91 tput cup 9 38; tput el # Apaga (úsica da tela
92 tput cup 11 38; tput el # Apaga Artista da tela
93 done
94 # Gra a registro no fim do arqui o
95 echo $”eg >> musicas
96 # Classifica o arqui o
97 sort musicas -o musicas
98 done
papodebotequim LinuxUser
www.linuxmagazine.com.br
maio2005 edição08 89
Ahh!Agorasim!Quandopassadocomoparâmetrodocomandosource,oscript foiexecutadonoshellcor-rente,deixandoneletodooambientecriado.Agoravamosrebobinarafitaatéoiníciodaexplicaçãosobreestecomando.Láfalamosdo.bash_pro-filee,aestaaltura,vocêjádevesaberquesuaincumbênciaé,logoapósologin,prepararoambientedetrabalhoparaousuário.Agoraentendemosqueéporissomesmoqueeleéexecutadousandoesseartifício.
E agora você deve estar se per-guntandoseésóparaissoqueessecomandoserve.Eulhedigoquesim,masissonostrazummontedevanta-gens–eumadasmaisusadasétratarfunçõescomorotinasexternas.Vejanalistagem 5umaoutraformadefazeronossoprogramaparaincluirCDsnoarquivomusicas.
Agoraoprogramadeuumaboaenco-lhidaeaschamadasdefunçãoforamtrocadasporarquivosexternoschama-dospergunta.funcemandamsg.func,queassimpodemserchamadosporqualqueroutroprograma,dessaformareutilizandooseucódigo.
Pormotivosmeramentedidáticos,aschamadasapergunta.funcemanda-msg.funcestãosendofeitasporsourceepor.(ponto)indiscriminadamente,emboraeuprefiraosourceque,porsermaisvisível,melhoraalegibili-dadedocódigoefacilitasuaposteriormanutenção.Vejanalistagem6comoficaramessesdoisarquivos.
Emambososarquivos,fizsomenteduas mudanças, que veremos nasobservaçõesaseguir.Porém,tenhomaistrêsobservaçõesafazer:1. Asvariáveisnãoestãosendomaisdeclaradascomolocais,porqueessaéumadiretivaquesópodeserusadanocorpodefunçõese,portanto,essasvariáveispermanecemnoambientedoshell,poluindo-o;2. Ocomandoreturnnãoestámaispresente,maspoderiaestarsemalte-raremnadaalógicadoscript,umavezquesóserviriaparaindicarumeventualerropormeiodeumcódigoderetornopreviamenteestabelecido(porexemploreturn 1,return 2,…),sendoqueoreturnereturn 0sãoidênticosesignificamquearotinafoiexecutadasemerros;
Listagem 5: musinc7.sh
01 $ cat musinc7.sh02 #!/bin/bash03 # Cadastra CDs ersao 704 #05 # Área de ariá eis globais06 Linha(esg=$ `tput lines` - 3 # Linha onde serão mostradas as msgs para o operador07 TotCols=$ tput cols # “td colunas da tela para enquadrar msgs08 # O corpo do programa propriamente dito começa aqui09 clear10 echo Inclusão de (úsicas ======== == ======= Título do Álbum: | Este campo foi Fai a: < criado somente para | orientar o preenchimento )ome da (úsica: Intérprete: # Tela montada com um único echo11 hile true12 do13 tput cup 5 38; tput el # Posiciona e limpa linha14 read Album15 [ ! $Album ] && # Operador deu <E)TE”>16 {17 source pergunta.func Deseja Terminar s n18 [ $S) = n ] && continue # Agora só testo a cai a bai a19 clear; e it # Fim da e ecução20 }21 grep -iq ^$Album\^ musicas 2> /de /null &&22 {23 . mandamsg.func Este álbum já está cadastrado24 continue # Volta para ler outro álbum25 }26 ”eg= $Album^ # $”eg receberá os dados de gra ação27 oArtista= # Guardará artista anterior28 hile true29 do30 Fai a++31 tput cup 7 3832 echo $Fai a33 tput cup 9 38 # Posiciona para ler música34 read (usica35 [ $(usica ] || # Se o operador ti er dado <E)TE”>...36 {37 . pergunta.func Fim de Álbum? s n38 [ $S) = n ] && continue # Agora só testo a cai a bai a39 break # Sai do loop para gra ar dados40 }41 tput cup 11 38 # Posiciona para ler Artista42 [ $oArtista ] && echo -n $oArtista # Artista anterior é default43 read Artista44 [ $Artista ] && oArtista= $Artista45 ”eg= $”eg$oArtista~$(usica: # (ontando registro46 tput cup 9 38; tput el # Apaga (úsica da tela47 tput cup 11 38; tput el # Apaga Artista da tela48 done49 echo $”eg >> musicas # Gra a registro no fim do arqui o50 sort musicas -o musicas # Classifica o arqui o51 done
LinuxUser
90
Papodebotequim
www.linuxmagazine.com.br
maio2005 edição08
3. Ocomandoqueestamosacostumadosausarparagerarumcódigoderetornoéoexit,masasaídadeumarotinaexternanãopodeserfeitadessaformaporque,comoelaestásendoexecutadanomesmoshelldoscriptqueocha-mou,oexitsimplesmenteencerrariaesseshell,terminandoaexecuçãodetodooscript;
4. DeondeveioavariávelLinha(esg?Elaveiodoscriptmusinc7.sh,porquehaviasidodeclaradaantesdachamadadasrotinas(nuncaesqueçaqueoshellqueestáinterpretandooscripteessasroti-naséomesmo);
5. Sevocêdecidirusarrotinasexternasnãoseenvergonhe,exagerenoscomen-tários,principalmentesobreapassagemdosparâmetros,parafacilitaramanu-tençãoeousodessarotinaporoutrosprogramasnofuturo.
–Bem, agora você já tem mais ummontedenovidadesparamelhorarosscriptsquefizemos.Vocêselem-bradoprogramalistartista.shnoqualvocêpassavaonomedeumartistacomoparâmetroeeledevolviaassuasmúsicas?Eleeracomoomostradoaquiembaixonalistagem 7.
–Claroquemelembro!–Parafirmarosconceitosquetepas-
sei,faça-ocomatelaformatadaeaexecuçãoemloop,deformaqueelesóterminequandoreceberumEnternolugardonomedoartista.Suponhaqueatelatenha25linhas;acada22músicaslistadasoprogramadeverádarumaparadaparaqueooperadorpossalê-las.Eventuaismensagensdeerrodevemserpassadasusandoarotinamandamsg.funcqueacabamosdedesenvolver.Chico,mandamaisdois!!Omeuécompoucapressão…
Nãoseesqueça:emcasodedúvidaou faltadecompa-nhiaparaumchope,ésómandarume-mailparaoendereçojulio.neves@gmail.comquetereiprazeremlheajudar.Vouaproveitartambémparamandarminhapropaganda:digaaosamigosquequemestiverafimdefazerumcursoporretadeprogramaçãoemShelldevemandarume-mailparajulio.neves@tecnohall.com.brparainformar-se.Atémais! ■
Listagem 6: pergunta.func e mandamsg.func
01 $ cat pergunta.func
02 # A função recebe 3 parâmetros na seguinte ordem:
03 # $1 - (ensagem a ser dada na tela
04 # $2 - Valor a ser aceito com resposta default
05 # $3 - O outro alor aceito
06 # Supondo que $1=Aceita?, $2=s e $3=n, a linha
07 # abai o colocaria em (sg o alor Aceita? S/n
08 (sg= $1 `echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z`
09 Tam(sg=${#(sg}
10 Col=$ TotCols - Tam(sg / 2 # Centraliza msg na linha
11 tput cup $Linha(esg $Col
12 echo $(sg
13 tput cup $Linha(esg $ Col + Tam(sg + 1
14 read -n1 S)
15 [ ! $S) ] && S)=$2 # Se azia coloca default em S)
16 echo $S) | tr A-Z a-z # A saída de S) será em minúscula
17 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
18 $ cat mandamsg.func
19 # A função recebe somente um parâmetro
20 # com a mensagem que se deseja e ibir;
21 # para não obrigar o programador a passar
22 # a msg entre aspas, usaremos $* todos
23 # os parâmetro, lembra? e não $1.
24 (sg= $*
25 Tam(sg=${#(sg}
26 Col=$ TotCols - Tam(sg / 2 # Centraliza msg na linha
27 tput cup $Linha(esg $Col
28 echo $(sg
29 read -n1
30 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
Listagem 7: listartista.sh
01 $ cat listartista.sh
02 #!/bin/bash
03 # Dado um artista, mostra as suas musicas
04 # ersao 2
05 if [ $# -eq 0 ]
06 then
07 echo Voce de eria ter passado pelo menos um parametro
08 e it 1
09 fi
10 IFS=
11 :
12 for Art(us in $ cut -f2 -d^ musicas
13 do
14 echo $Art(us | grep -i ^$*~ > /de /null && echo $Art(us | cut -f2 -d~
15 done
Informações
[1] Bash, página oficial:
http://www.gnu.org/software/bash/bash.html
[2] Manual de referência do Bash:
http://www.gnu.org/software/bash/
manual/bashref.html
So
br
eo
au
tor Julio Cezar Neves é Analista de Suporte de
Sistemas desde 1969 e trabalha com Unix
desde 1980, quando participou do desen-
volvimento do SOX, um sistema operacio-
nal similar ao Unix produzido pela Cobra
Computadores. Pode ser contatado no
e-mail [email protected]
papodebotequim LinuxUser
www.linuxmagazine.com.br
maio2005 edição08 91
Tábom,jáseiquevocêvaiquererchopeantesdecomeçar,
mastôtãoafimdetemostraroquefizquejávoupedira
rodadaenquantoisso.AêChico,mandadois!Odeleésem
colarinhopranãodeixarcheiroruimnessebigodão…
Enquantoochopenãochega,deixaeutelembraroque
vocêmepediunaediçãopassada:erapararefazer
oprogramalistartistacomatelaformatadaeexe-
cuçãoemloop,deformaqueelesóterminequando
receberum[ENTER]sozinhocomonomedoartista.
Eventuaismensagensdeerroeperguntasfeitasao
usuáriodeveriamsermostradasnaantepenúltima
linhadatela,utilizandoparaissoasrotinasexternas
mandamsg.funcepergunta.funcquedesenvolvemos
durantenossopaponaediçãopassada.
Primeiramenteeudeiumaencolhidanasrotinas
mandamsg.funcepergunta.func,queficaramcomo
nalistagem 1.Enalistagem 2vocêtemo“grandão”,
nossaversãorefeitadolistaartista.
–Puxa,vocêchegoucomacordatoda!Gosteida
formacomovocêresolveuoproblemaeestruturou
oprograma.Foimaistrabalhoso,masaapresenta-
çãoficoumuitolegalevocêexploroubastanteas
opçõesdocomandotput.Vamostestaroresultado
comumálbumdoEmerson,Lake&Palmerque
tenhocadastrado.
Envenenando a escritaUfa!Agoravocêjásabetudosobreleituradedados,
masquantoàescritaaindaestáapenasengatinhando.
Jáseiquevocêvaimeperguntar:“Ora,nãoécomo
comandoechoecomosredirecionamentosdesaída
queseescrevedados?”.
Bom,arespostaé"maisoumenos".Comestescomandos
vocêescreve90%doqueprecisa,porémseprecisarescrever
algoformatadoeleslhedarãomuitotrabalho.Paraformatara
saídaveremosagoraumainstruçãomuitomaisinteressante,
aprintf.Suasintaxeéaseguinte:
Hoje vamos aprender mais sobre formatação de cadeias de caracteres,
conhecer as principais variáveis do Shell e nos aventurar no (ainda)
desconhecido território da expansão de parâmetros. E dá-lhe chope!
porJúlioCezarNeves
Curso de Shell Script
PapodeBotequimParteIX
Da
ve
Ha
milt
on
- ww
w.s
xc
.hu
Listagem 1: mandamsg.func e pergunta.func
mandamsg.func01 # A função recebe somente um parâmetro
02 # com a mensagem que se deseja e ibir.
03 # Para não obrigar o programador a passar
04 # a msg entre aspas, usaremos $* todos
05 # os parâmetro, lembra? e não $1.
06 (sg="$*"
07 Tam(sg=${#(sg}
08 Col=$ TotCols - Tam(sg / 2 # Centra msg na linha
09 tput cup $Linha(esg $Col
10 read -n1 -p "$(sg "
pergunta.func01 # A função recebe 3 parâmetros na seguinte ordem:
02 # $1 - (ensagem a ser mostrada na tela
03 # $2 - Valor a ser aceito com resposta padrão
04 # $3 - O outro alor aceito
05 # ‘upondo que $1=Aceita?, $2=s e $3=n, a linha
06 # abai o colocaria em (sg o alor "Aceita? ‘/n "
07 (sg="$1 `echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z` "
08 Tam(sg=${#(sg}
09 Col=$ TotCols - Tam(sg / 2 # Centraliza msg na linha
10 tput cup $Linha(esg $Col
11 read -n1 -p "$(sg " ‘)
12 [ ! $‘) ] && ‘)=$2 # ‘e azia coloca default em ‘)
13 ‘)=$ echo $‘) | tr A-Z a-z # A saída de ‘) será em minúscula
14 tput cup $Linha(esg $Col; tput el # Apaga msg da tela
LinuxUser
84
PapodeBotequim
www.linuxmagazine.com.br
junho2005 edição09
printf formato [argumento...]
Ondeformatoéumacadeia
decaracteresquecontémtrês
tipos de objeto: caracteres
simples,caracteresparaes-
pecificaçãodeformato(oude
controle)eseqüênciadeesca-
penopadrãodalinguagem
C.argumentoéacadeiade
caracteresaserimpressasob
ocontroledeformato.
Cadaumdoscaracteresuti-
lizadoséprecedidopeloca-
racter%e,logoaseguir,vem
aespecificaçãodeformatode
acordocomatabela 1.
As seqüências de escape
padrãodalinguagemCsão
sempre precedidas pelo ca-
racterecontra-barra(\).As
reconhecidaspelocomando
printfsãoasdatabela 2.
Nãoacabaporaínão!Tem
muitomaiscoisasobreessa
instrução,mascomoesseé
umassuntomuitocheiode
detalhese,portanto, chato
paraexplicarepioraindapara
lerouestudar,vamospassar
diretoaosexemploscomco-
mentários.Vejasó:
$ printf "%c" "1 caracter"
1$
Errado!Sólistou1caractere
enãosaltoulinhaaofinal
$ printf "%c\n" "1 caracter"
1
Saltoulinhamasaindanão
listouacadeiainteira
$ printf "%c caractere\n" 1
1 caractere ➟
Listagem 2: listaartista
$ cat listartista3.sh01 #!/bin/bash02 # Dado um artista, mostra as suas musicas03 # ersao 304 Linha(esg=$ `tput lines` - 3 # Linha onde as msgs serão mostradas05 TotCols=$ tput cols # Qtd de colunas na tela para enquadrar msgs06 clear07 echo " +–––––––––––––––––-----------------------------------+ | Lista Todas as (úsicas de um Determinado Artista | | ===== ===== == ======= == == =========== ======= | | | | Informe o Artista: | +––––––––––––––––----------------------------------–-+"08 hile true09 do10 tput cup 5 51; tput ech 31 # ech=Erase chars 31 para não apagar barra ertical11 read )ome12 if [ ! "$)ome" ] # $)ome esta azio?13 then14 . pergunta.func "Deseja ‘air?" s n15 [ $‘) = n ] && continue16 break17 fi18 fgrep -iq "^$)ome~" musicas || # fgrep não interpreta ^ como e pressão regular19 {20 . mandamsg.func ")ão e iste música desse artista"21 continue22 }23 tput cup 7 29; echo '| |'24 LinAtual=825 IF‘="26 :"27 for Art(us in $ cut -f2 -d^ musicas # E clui nome do album28 do29 if echo "$Art(us" | grep -iq "^$)ome~"30 then31 tput cup $LinAtual 2932 echo -n '| "33 echo $Art(us | cut -f2 -d~34 tput cup $LinAtual 8235 echo '|'36 let LinAtual++37 if [ $LinAtual -eq $Linha(esg ]38 then39 . mandamsg.func "Tecle Algo para Continuar..."40 tput cup 7 0; tput ed # Apaga a tela a partir da linha 741 tput cup 7 29; echo '| |'42 LinAtual=843 fi44 fi45 done46 tput cup $LinAtual 29; echo '| |'47 tput cup $ ++LinAtual 2948 read -n1 -p "+–––--Tecle Algo para )o a Consulta––––+"49 tput cup 7 0; tput ed # Apaga a tela a partir da linha 750 done
www.linuxmagazine.com.br
junho2005 edição09 85
PapodeBotequim LinuxUser
Opa,essaéaformacorreta!O%crece-
beuovalor1,comoqueríamos:
$ a=2
$ printf "%c caracteres\n" $a
2 caracteres
O%crecebeuovalordavariável$a.
$ printf "%10c caracteres\n" $a
2 caracteres
$ printf "%10c\n" $a caracteres
2
c
Repareque,nosdoisúltimosexemplos,
emvirtudedousodo%c,sófoilistado
umcaracteredecadacadeiadecaracteres
passadacomoparâmetro.Ovalor10à
frentedocnãosignifica10caracteres.Um
númeroseguindoosinaldepercentagem
(%)significaotamanhoqueacadeiaterá
apósaexecuçãodocomando.Vamosver
aseguirmaisalgunsexemplos.Osco-
mandosabaixo:
$ printf "%d\n" 32
32
$ printf "%10d\n" 32
32
preenchemastringcomespaçosem
brancoàesquerda(oitoespaçosmaisdois
caracteres,10dígitos),nãocomzeros.Já
nocomandoabaixo:
$ printf "%04d\n" 32
0032
O04após%significa“formateastring
emquatrodígitos,comzerosàesquerda
senecessário”.Nocomando:
$ printf "%e\n" $ echo "scale=2 ; 100/6" | bc
1.666000e+01
Opadrãodo%eéseiscasasdecimais.
Jánocomando:
$ printf "%.2e\n" `echo "scale=2 ; 100/6" | bc`
1.67e+01
O.2especificouduascasasdecimais.
Observeagora:
$ printf "%f\n" 32.3
32.300000
Opadrãodo%féseiscasasdecimais.
Enocomando:
$ printf "%.2f\n" 32.3
32.30
O.2especificouduascasasdecimais.
Agoraobserve:
$ printf "%.3f\n" `echo "scale=2 ; 100/6" | bc`
33.330
Obcdevolveuduascasasdecimaise
oprintfcolocouozeroàdireita.Oco-
mandoaseguir:
$ printf "%o\n" 10
12
Converteuovalor10parabaseoctal.
Paramelhorarexperimente:
$ printf "%03o\n" 27
033
Assimaconversãoficacommaisjeito
deoctal,né?.Oqueesteaquifaz?
$ printf "%s\n" Peteleca
Peteleca
$ printf "%15s\n" Peteleca
Peteleca
ImprimePetelecacom15caracteres.
Acadeiadecaracteresépreenchidacom
espaçosembrancoàesquerda.Jánoco-
mando:
$ printf "%-15s)e es\n" Peteleca
Peteleca )e es
Omenos(-)colocouespaçosembranco
àdireitadePetelecaatécompletaros15
caracterespedidos.Eocomandoabaixo,
oquefaz?
$ printf "%.3s\n" Peteleca
Pet
O.3mandatruncaracadeiadeca-
racteresapósastrêsprimeirasletras.E
ocomandoaseguir:
$ printf "%10.3sa\n" Peteleca
Peta Pet
Imprimeacadeiacom10caracteres,
truncadaapósostrêsprimeiros,conca-
tenadacomocaracterea(apósos).E
essecomandoaseguir,oquefaz?
Tabela 1: Formatos de caractere
Caractere A expressão será impressa como:
c Caractere simples
d Número no sistema decimal
e Notação científica exponencial
f Número com ponto decimal (float)
g O menor entre os formatos %e e %f com omissão dos zeros não significativos
o Número no sistema octal
s Cadeia de caracteres
x Número no sistema hexadecimal
% Imprime um %. Não há nenhum tipo de conversão
Tabela 2: Seqüências de escape
Seqüência Efeito
a Soa o beep
b Volta uma posição (backspace)
f Salta para a próxima página lógica ( form feed)
n Salta para o início da linha se-guinte (line feed)
r Volta para o início da linha cor-rente (carriage return)
t Avança para a próxima marca de tabulação
LinuxUser
86
PapodeBotequim
www.linuxmagazine.com.br
junho2005 edição09
$ printf EXE(PLO % \n 45232
EXE(PLO b0b0
Eletransformouonúmero45232para
hexadecimal(b0b0),masoszerosnão
combinamcomoresto.Experimente:
$ printf EXE(PLO %X\n 45232
EXE(PLO B0B0
Assimdisfarçoumelhor!(reparenoX
maiúsculo).Praterminar,quetaloco-
mandoabaixo:
$ printf %X %XL%X\n 49354 192 10
C0CA C0LA
Esteaínãoémarketingeébastante
completo,vejasócomofunciona:
Oprimeiro%Xconverteu49354emhe-
xadecimal,resultandoemC0CA(leia-se
“cê”,“zero”,“cê”e“a”).Emseguidaveio
umespaçoembrancoseguidoporoutro
%XL.O%Xconverteuo192dandocomo
resultadoC0quecomoLfezC0L.Efinal-
menteoúltimoparâmetro%Xtransformou
onúmero10naletraA.
Conformevocêspodemnotar,ainstru-
çãoébastantecompletaecomplexa.Ain-
dabemqueoechoresolvequasetudo...
Acerteiemcheioquandoresolviexpli-
caroprintfatravésdeexemplos,pois
nãosaberiacomoenumerartantasregri-
nhassemtornaraleituraenfadonha.
Principais variáveis do ShellOBashpossuidiversasvariáveisque
servem para dar informações sobre
oambienteoualterá-lo.Sãomuitase
nãopretendomostrartodaselas,mas
umapequenapartepodelheajudarna
elaboraçãodescripts.Vejaaseguiras
principaisdelas:
CDPATH»Contémoscaminhosque
serãopesquisadosparatentarlocalizar
umdiretórioespecificado.Apesardessa
variávelserpoucoconhecida,seuuso
deveserincentivadoporpouparmuito
trabalho,principalmenteeminstalações
comestruturadediretóriosemmúltiplos
níveis.Vejaoexemploaseguir:
$ echo $CDPATH
.:..:~:/usr/local
$ p d
/home/jne es/L(
$ cd bin
$ p d
/usr/local/bin
Como/usr/localestavanaminha
variável$CDPATHenãoexistiaodiretório
binemnenhumdosseusantecessores(.,
..e~),ocomandocdfoiexecutadotendo
comodestino/usr/local/bin.
HI‘T‘IZE»Limitaonúmerodeins-
truçõesquecabemdentrodoarquivode
históricodecomandos(normalmente
.bash_histor ,masnaverdadeéoque
estáindicadonavariável$HI‘TFILE).Seu
valorpadrãoé500.
HO‘T)A(E»Onomedohostcorrente
(quetambémpodeserobtidocomoco-
mandouname -n).
LA)G»Usadaparadeterminaroidioma
faladonopaís(maisespecificamenteca-
tegoriadolocale).Vejaumexemplo:
$ date
Thu Apr 14 11:54:13 B”T 2005
$ LA)G=pt_B” date
“ui Abr 14 11:55:14 B”T 2005
LI)E)O»Onúmerodalinhadoscript
ou funçãoque está sendo executada.
Seuusoprincipalémostrarmensagens
deerrojuntamentecomasvariáveis$0
(nomedoprograma)e$FU)C)A(E(nome
dafunçãoemexecução).
LOG)A(E»Estavariávelarmazenao
nomedelogindousuário.
(AILCHECK»Especifica,emsegundos,a
freqüênciacomqueoShellverificaapre-
sençadecorrespondêncianosarquivos
indicadospelavariáveis$(AILPATHou
$(AIL.Otempopadrãoéde60segundos
(1minuto).AcadaintervalooShellfará
averificaçãoantesdeexibiropróximo
promptprimário($P‘1).Seessavariável
estiversemvaloroucomumvalormenor
ouigualazero,abuscapornovasmen-
sagensnãoseráefetuada.
PATH»Caminhosqueserãopesquisa-
dosparatentarlocalizarumarquivoespe-
cificado.Comocadascriptéumarquivo,
casouseodiretóriocorrente(.)nasua
variável$PATH,vocênãonecessitaráusar
ocomando./scrpparaqueoscriptscrp
sejaexecutado.Bastadigitarscrp.Este
éomodoqueprefiro.
PIPE‘TATU‘»Éumavariáveldotipo
vetor(array)quecontémumalistade
valoresdecódigosderetornodoúltimo
pipelineexecutado,istoé,umarrayque
abrigacadaumdos$?decadainstrução
doúltimopipeline.Paraentendermelhor,
vejaoexemploaseguir:
$ ho
jne es pts/0 Apr 11 16:26 10.2.4.144
jne es pts/1 Apr 12 12:04 10.2.4.144
$ ho | grep ^botelho
$ echo ${PIPESTATUS[*]}
0 1
Nesteexemplomostramosqueousu-
áriobotelhonãoestava“logado”,em
seguidaexecutamosumpipelineque
procuravaporele.Usa-seanotação[*]
emumarrayparalistartodososseus
elementos;dessaforma,vimosqueapri-
meirainstrução( ho)foibem-sucedida
(códigoderetorno0)easeguinte(grep)
não(códigoderetorno1).
PRO(PT_CO((A)D»Seestavariávelre-
ceberonomedeumcomando,todavez
quevocêteclarum[ENTER]sozinhono
promptprincipal($P‘1),essecomando
seráexecutado.Émuitoútilquandovocê
precisarepetindoconstantementeuma
determinadainstrução.
P‘1»Éopromptprincipal.No“Papo
deBotequim”usamosospadrões$para
usuáriocomume#pararoot,masémui-
www.linuxmagazine.com.br
junho2005 edição09 87
PapodeBotequim LinuxUser
tofreqüentequeeleestejapersonalizado.
Umacuriosidadeéqueexisteatéconcurso
dequemprogramao$P‘1maiscriativo.
P‘2»Tambémchamado“promptde
continuação”,éaquelesinaldemaior
(>)queapareceapósum[ENTER]sem
ocomandotersidoencerrado.
PWD » Possui o caminho completo
($PATH) dodiretório corrente. Temo
mesmoefeitodocomandop d.
RA)DO(»Cadavezqueestavariávelé
acessada,devolveuminteiroaleatórioen-
tre0e32767.Parageraruminteiroentre
0e100,porexemplo,digitamos:
$ echo $ ”A)DO(%101
73
Ouseja,pegamosorestodadivisãodo
númerorandômicogeradopor101porque
orestodadivisãodequalquernúmero
por101variaentre0e100.
REPLY»Useestavariávelpararecuperar
oúltimocampolido,casoelenãotenha
nenhumavariávelassociada.Exemplo:
$ read -p "Digite S ou ): "
Digite S ou ): )
$ echo $”EPLY
)
‘ECO)D‘»Estavariávelinforma,emse-
gundos,háquantotempooShellcorrente
está“depé”.Use-aparademonstrara
estabilidadedoLinuxeesnobarusuários
daquelacoisacomjanelinhascoloridas
quechamamdesistemaoperacional,mas
quenecessitade“reboots”freqüentes.
T(OUT»Seestavariávelcontiverum
valor maior do que zero, esse valor
seráconsideradootimeoutpadrãodo
comandoread.Noprompt,essevalor
éinterpretadocomootempodeespera
porumaaçãoantesdeexpirarasessão.
Supondoqueavariávelcontenhaovalor
30,oShellencerraráasessãodousuário
(ouseja,farálogout)após30segundos
semnenhumaaçãonoprompt.
Expansão de parâmetrosBem,muitodoquevimosatéagorasão
comandosexternosaoShell.Elesquebram
omaiorgalho,facilitamavisualização,
manutençãoedepuraçãodocódigo,mas
nãosãotãoeficientesquantoosintrínse-
cos(built-ins).Quandoonossoproblema
forperformance,devemosdarpreferência
aousodosintrínsecos.Apartirdeagora
voutemostraralgumastécnicasparao
seuprogramapisarnoacelerador.
Natabela 3enosexemplosaseguir,
veremosumasériedeconstruçõescha-
madasexpansão(ousubstituição)de
parâmetros(ParameterExpansion),que
substitueminstruçõescomoocut,oe pr,
otr,osedeoutrasdeformamaiságil.
Vamosveralgunsexemplos:seemuma
perguntao‘éoferecidocomovalorde-
fault(padrão)easaídavaiparaavariável
‘),apóslerovalorpodemosfazer:
S)=$ S):-S}
Parasaberotamanhodeumacadeia:
$ cadeia=0123
$ echo ${#cadeia}
4
Paraextrairdadosdecadeia,daposi-
çãoumatéofinalfazemos:
$ cadeia=abcdef
$ echo ${cadeia:1}
bcdef
Reparequeaorigemézeroenãoum.
Vamosextrair3caracteresapartirda2ª
posiçãodamesmavariávelcadeia:
$ echo ${cadeia:2:3}
cde
Reparequenovamenteaorigemdacon-
tagemézeroenãoum.Parasuprimir
tudoàesquerdadaprimeiraocorrência
deumacadeia,faça:
$ cadeia="Papo de Botequim"
$ echo ${cadeia#*' '}
de Botequim
$ echo "Con ersa "${cadeia#*' '}
Con ersa de Botequim
Noexemploanteriorfoisuprimidoà
esquerdatudooque“casa”comamenor
ocorrênciadaexpressão* ,ouseja,
todososcaracteresatéoprimeiroespaço
embranco.Essesexemplostambémpo-
deriamserescritossemprotegeroespaço
dainterpretaçãodoShell(masprefiro
protegê-loparafacilitaralegibilidade
docódigo).Vejasó:
$ echo ${cadeia#* }
de Botequim
$ echo "Con ersa "${cadeia#* }
Con ersa de Botequim
Reparequenaconstruçãodee pré
permitidoousodemetacaracteres.
Utilizandoomesmovalordavariável
cadeia,observecomofaríamosparater
somenteBotequim:
$ echo ${cadeia##*' '}
Botequim
$ echo "Vamos 'Chopear' no "${cadeia##*' '}
Vamos 'Chopear' no Botequim
Destavezsuprimimosàesquerdade
cadeiaamaiorocorrênciadaexpressão
e pr.Assimcomonocasoanterior,ouso
demetacaracteresépermitido.
Outroexemplomaisútil:paraquenão
apareçaocaminho(path)completodoseu
programa($0)emumamensagemdeerro,
inicieoseutextodaseguinteforma:
echo Uso: ${0##*/} te to da mensagem de erro
Nesteexemploseriasuprimidoàes-
querdatudoatéaúltimabarra(/)do
caminho,restandosomenteonomedo
programa.Ocaractere%ésimétricoao
#,vejaoexemplo:
LinuxUser
88
PapodeBotequim
www.linuxmagazine.com.br
junho2005 edição09
$ echo $cadeia
Papo de Botequim
$ echo ${cadeia%' '*}
Papo de
$ echo ${cadeia%%' '*}
Papo
Paratrocarprimeiraocorrênciadeuma
subcadeiaemumacadeiaporoutra:
$ echo $cadeia
Papo de Botequim
$ echo ${cadeia/de/no}
Papo no Botequim
$ echo ${cadeia/de /}
Papo Botequim
Presteatençãoquandoforusarmetaca-
racteres!Elessãogulososesemprecom-
binarãocomamaiorpossibilidade;No
exemploaseguireuqueriatrocarPapode
BotequimporConversadeBotequim:
$ echo $cadeia
Papo de Botequim
$ echo ${cadeia/*o/Con ersa}
Con ersatequim
Aidéiaerapegartudoatéoprimeiro
o,masacabousendotrocadotudoatéo
últimoo.Istopoderiaserresolvidode
diversasmaneiras.Eisalgumas:
$ echo ${cadeia/*po/Con ersa}
Con ersa de Botequim
$ echo ${cadeia/????/Con ersa}
Con ersa de Botequim
Trocandotodasasocorrênciasdeuma
subcadeiaporoutra.Ocomando:
$ echo ${cadeia//o/a}
Papa de Batequim
Ordenaatrocadetodosasletrasopor
a.Outroexemplomaisútiléparacontar
aquantidadedearquivosexistentesno
diretóriocorrente.Observeoexemplo:
$ ls | c -l
30
O cpõeummontedeespaçosem
brancoantesdoresultado.Paratirá-los:
# “tdArqs recebe a saída do comando
$ “tdArqs=$ ls | c -l
$ echo ${“tdArqs/ * /}
30
Nesseexemplo,eusabiaqueasaídaera
compostadebrancosenúmeros,porisso
monteiessaexpressãoparatrocartodosos
espaçospornada.Notequeanteseapóso
asterisco(*)háespaçosembranco.
Háváriasformasdetrocarumasubca-
deianoinícioounofimdeumavariável.
Paratrocarnoiníciofazemos:
$ echo $Passaro
quero quero
$ echo Como diz o sulista - ${PassaroU
/#quero/não}
Como diz o sulista - não quero
Paratrocarnofinalfazemos:
$ echo Como diz o nordestino - U
${Passaro/%quero/não}
Como diz o nordestino - quero não
–Agorajáchega,opapohojefoichato
porquetevemuitadecoreba,masoque
maisimportaévocêterentendidoo
quetefalei.Quandoprecisar,consulte
estesguardanaposonderabisqueias
dicasedepoisguarde-osparaconsultas
futuras.Masvoltandoàvacafria:tá
nahoradetomaroutroeverojogodo
Mengão.Prapróximavezvoutedar
molezaesóvoucobraroseguinte:pe-
guearotinapergunta.func(daqual
falamosnoiníciodonossobate-papo
dehoje,vejaalistagem 1)eotimize-a
paraqueavariável$‘)recebaovalor
padrãoporexpansãodeparâmetros,
comovimos.
Enãoseesqueça:emcasodedúvidas
oufaltadecompanhiaparaum(oumais)
chopeésómandarume-mailparajulio.
quequemestiverafimdefazerumcurso
porretadeprogramaçãoemShelldeve
mandarume-mailparajulio.neves@tecnohall.
com.brparainformar-se.Valeu! ■
Tabela 3: Tipos de expansão de parâmetros
Expansão de parâmetros Resultado esperado
${var:-padrao} Se ar é vazia, o resultado da expressão é padrão
${#cadeia} Tamanho de $cadeia
${cadeia:posicao} Extrai uma subcadeia de $cadeia a partir de posição. Origem zero
${cadeia:posicao:tamanho} Extrai uma subcadeia de $cadeia a partir de posição com tamanho igual a tamanho. Origem zero
${cadeia#expr} Corta a menor ocorrência de $cadeia à esquerda da expressão e pr
${cadeia##expr} Corta a maior ocorrência de $cadeia à esquerda da expressão e pr
${cadeia%expr} Corta a menor ocorrência de $cadeia à direita da expressão e pr
${cadeia%%expr} Corta a maior ocorrência de $cadeia à direita da expressão e pr
${cadeia/subcad1/subcad2} Troca a primeira ocorrência de subcad1 por subcad2
${cadeia//subcad1/subcad2} Troca todas as ocorrências de subcad1 por subcad2
${cadeia/#subcad1/subcad2} Se subcad1 combina com o início de cadeia, então é trocado por subcad2
${cadeia/%subcad1/subcad2} Se subcad1 combina com o fim de cadeia, então é trocado por subcad2
So
br
eo
au
tor Julio Cezar Neves é Analista de Suporte de
Sistemas desde 1969 e trabalha com Unix
desde 1980, quando participou do desen-
volvimento do SOX, um sistema operacio-
nal similar ao Unix produzido pela Cobra
Computadores. Pode ser contatado no
e-mail [email protected]
www.linuxmagazine.com.br
junho2005 edição09 89
PapodeBotequim LinuxUser
E aêamigo,tedeiamaiormolezana
últimaaulané?Umexerciciozinho
muitosimples…
–É,masnostestesqueeufiz,ede
acordocomoquevocêensinousobre
substituiçãodeparâmetros,acheique
deveriafazeralgumasalteraçõesnasfun-
çõesquedesenvolvemosparatorná-lasde
usogeral,comovocêdissequetodasas
funçõesdeveriamser.Querver?
–Claro,né,mané,setepediparafazer
éporqueestouafimdeteveraprender,
masperaí,dáumtempo.Chico!Manda
dois,umsemcolarinho!Vai,mostraaí
oquevocêfez.
–Bem,alémdoquevocêpediu,eu
repareiqueoprogramaquechamavaa
funçãoteriadeterpreviamentedefinidas
alinhaemqueseriamostradaamensa-
gemeaquantidadedecolunas.Oque
fizfoiincluirduaslinhas–nasquais
empregueisubstituiçãodeparâmetros
–paraque,casoumadessasvariáveisnão
fosseinformada,elarecebesseumvalor
atribuídopelaprópriafunção.Alinha
demensagemétrêslinhasantesdofim
datelaeototaldecolunaséobtidopelo
comando tput cols .Dêumaolhadana
listagem 1 evejacomoficou:
–Gostei,vocêjáseantecipouaoqueeu
iapedir.Sópragenteencerraressepapo
desubstituiçãodeparâmetros,repareque
alegibilidadedocódigoestá“horrorível”,
masodesempenho,istoé,avelocidade
deexecução,estáótimo.Comofunções
sãocoisasmuitopessoais,jáquecada
umusaassuasequasenãoháneces-
sidadedemanutenção,eusempreopto
pelodesempenho.
Hojevamossairdaquelachaturaque
foionossoúltimopapoevoltaràlógica,
saindodadecoreba.Masvoltoatelem-
brar:tudoqueeutemostreidaúltimavez
aquinoBotecodoChicoéválidoequebra
umgalhão.Guardeaquelesguardanapos
querabiscamosporque,maiscedoou
maistarde,eleslhevãosermuitoúteis.
O comando eval Voutedarumproblemaqueeuduvido
quevocêresolva:
$ var1=3
$ var2=var1
Tedeiessasduasvariáveisequeroque
vocêmedigacomoeuposso,mereferindo
apenasàvariávela ar2 ,listarovalorde
ar1 (que,nonossocaso,é3).
–Ah,issoémole,mole!Ésódigitar
essecomandoaqui:
echo $`echo $var2`
Listagem 1: função pergunta.func
01 # A função recebe 3 parâmetros na seguinte ordem:
02 # $1 - (ensagem a ser mostrada na tela
03 # $2 - Valor a ser aceito com resposta padrão
04 # $3 - O outro alor aceito
05 # Supondo que $1=Aceita?, $2=s e $3=n, a linha
06 # abai o colocaria em (sg o alor "Aceita? S/n "
07 TotCols=${TotCols:-$ tput cols } # Se não esta a definido, agora está
08 Linha(esg=${Linha(esg:-$ $ tput lines -3 } # Idem
09 (sg="$1 `echo $2 | tr a-z A-Z`/`echo $3 | tr A-Z a-z` "
10 Tam(sg=${#(sg}
11 Col=$ TotCols - Tam(sg / 2 # Para centralizar (sg na linha
12 tput cup $Linha(esg $Col
13 read -n1 -p "$(sg " S)
14 S)=${S):-$2} # Se azia coloca o padrão em S)
15 S)=$ echo $S) | tr A-Z a-z # A saída de S) será em minúsculas
16 tput cup $Linha(esg $Col; tput el # Apaga (sg da tela
Em mais um capítulo de nossa saga através do mundo do
Shell Script, vamos aprender a avaliar expressões, capturar
sinais e receber parâmetros através da linha de comando.
porJúlioCezarNeves
Curso de Shell Script
PapodeBotequimParteX
Da
ve
Ha
milt
on
- ww
w.s
xc
.hu
LinuxUser
86
PapodeBotequim
www.linuxmagazine.com.br
julho2005 edição10
Reparequeeucoloqueioecho $ ar2entrecrases(`),porque
dessaformaeleteráprioridadedeexecuçãoeresultaráem ar1.
Eecho $ ar1produzirá3…
–Ah,é?Entãoexecuteparaverseestácorreto.
$ echo $`echo $var2`
$var1
–Ué!Quefoiqueaconteceu?Omeuraciocíniomeparecia
bastantelógico…
–Oseuraciocíniorealmentefoilógico,oproblemaéquevocê
esqueceudeumadasprimeirascoisasdequetefaleiaquino
Botecoequevourepetir.OShellusaaseguinteordempara
resolverumalinhadecomando:
PResolveosredirecionamentos;
PSubstituiasvariáveispelosseusvalores;
PResolveesubstituiosmetacaracteres;
PPassaalinhajátodaesmiuçadaparaexecução.
Dessaforma,quandoointerpretadorchegounafasedere-
soluçãodevariáveis,quecomoeudisseéanterioràexecução,
aúnicavariávelexistenteera ar2eporissoatuasolução
produziucomosaída$ ar1.Ocomandoechoidentificouisso
comoumacadeiadecaracteresenãocomoumavariável.
Problemasdessetiposãorelativamentefreqüenteseseriam
insolúveiscasonãoexistisseainstruçãoeval,cujasintaxeé
e al cmd,ondecmdéumalinhadecomandoqualquer,que
vocêpoderiainclusiveexecutardiretonopromptdoterminal.
Quandovocêpõeoevalnafrente,noentanto,oqueocorreé
queoShelltratacmdcomoumparâmetrodoevale,emseguida,
oevalexecutaalinharecebida,submetendo-aaoShell.Ou
seja,napráticacmdéanalisadoduasvezes.Dessaforma,se
executássemosocomandoquevocêpropôscolocandooeval
nafrente,teríamosasaídaesperada.Veja:
$ eval echo $`echo $var2`
3
Esseexemplotambémpoderiatersidofeitodeoutramaneira.
Dásóumaolhada:
$ eval echo \$$var2
3
Naprimeirapassadaacontrabarra(\)seriaretiradae$ ar2
seriaresolvidoproduzindo ar1.Nasegundapassadateriaso-
bradoecho $ ar1,queproduziriaoresultadoesperado.Agora
voucolocarumcomandodentrode ar2eexecutar:
$ var2=ls
$ $var2
10porpag1.sh alo2.sh incusu logado
10porpag2.sh ArqDoDOS.t t1 listamusica logaute.sh
10porpag3.sh confuso listartista mandamsg.func
alo1.sh contpal.sh listartista3 monbg.sh
Agoravamoscolocarem ar2oseguinte:ls $ ar1;eem
ar1vamoscolocarl*,vejamosoresultado:
$ var2='ls $var1'
$ var1='l*'
$ $var2
ls: $var1: No such file or director
$ eval $var2
listamusica listartista listartista3 logado logaute.sh
Novamente,notempodesubstituiçãodasvariáveis,$ ar1
aindanãohaviaseapresentadoaoShellparaserresolvida.
Assim,sónosrestaexecutarocomandoevalparadarasduas
passadasnecessárias.
Umavezumcolegadaexcelentelistadediscussãogroups.yahoo.com/
group/shell-scriptcolocouumadúvida:queriafazerummenu
quenumerasseelistassetodososarquivoscomextensão.she,
quandoooperadorescolhesseumaopção,oprogramacorres-
pondentefosseexecutado.Vejaminhapropostanalistagem 2 :
Listagem 2: fazmenu.sh
01 #!/bin/bash
02 #
03 # Lista que enumera os programas com e tensão .sh no
04 # diretório corrente e e ecuta o escolhido pelo operador
05 #
06 clear; i=1
07 printf "%11s\t%s\n\n" Opção Programa
08 CASE='case $opt in'
09 for arq in *.sh
10 do
11 printf "\t%03d\t%s\n" $i $arq
12 CASE="$CASE
13 "$ printf "%03d \t %s;;" $i $arq
14 i=$ i+1
15 done
16 CASE="$CASE
17 * . erro;;
18 esac"
19 read -n3 -p "Informe a opção desejada: " opt
20 echo
21 e al "$CASE"
www.linuxmagazine.com.br
julho2005 edição10 87
PapodeBotequim LinuxUser
Parececomplicadoporqueuseimuitos
printfparaformataçãodatela,masna
verdadeébastantesimples:oprimei-
roprintffoicolocadoparaimprimir
ocabeçalhoelogoemseguidacome-
ceiamontardinamicamenteavariável
$CASE,naqualaofinalseráfeitoumeval
paraexecuçãodoprogramaescolhido.
Reparenoentantoquedentrodoloop
doforexistemdoisprintf:oprimeiro
serveparaformataratelaeosegundo
paramontarocase(seantesdocoman-
doreadvocêcolocarumalinhaecho
"$CASE",veráqueocomandocasemon-
tadodentrodavariávelestátodoinden-
tado.Frescura,né?:).Nasaídadofor,foi
adicionadaumalinhaàvariável$CASE
para,nocasodeumaescolhainválida,
serexecutadaumafunçãoexternapara
exibirmensagensdeerro.Vamosexecu-
taroscriptparaverasaídagerada:
$ fazmenu.sh
Opcao Programa
001 10porpag1.sh
002 10porpag2.sh
003 10porpag3.sh
004 alo1.sh
005 alo2.sh
006 contpal.sh
007 fazmenu.sh
008 logaute.sh
009 monbg.sh
010 readpipe.sh
011 redirread.sh
Informe a opção desejada:
Seriainteressanteincluirumaopção
paraterminaroprogramae,paraisso,
serianecessáriaainclusãodeumalinha
apósoloopdemontagemdatelaeaalte-
raçãodalinhanaqualfazemosaatribui-
çãofinaldovalordavariável$CASE.Veja
nalistagem 3comoeleficaria:
ExistenoLinuxumacoisachamada
sinal(signal).Existemdiversossinais
quepodemsermandadospara(ougera-
dospor)processosemexecução.Vamos,
deagoraemdiante,darumaolhadinha
nossinaisenviadosaosprocessosemais
àfrentevamosdarumapassadarápida
pelossinaisgeradospelosprocessos.
Paramandarumsinalaumprocesso,
usamosnormalmenteocomandokill,
cujasintaxeé:
$ kill -sig PID
OndePIDéoidentificadordoproces-
so(ProcessIdentificationouProcessID).
Alémdocomandokill,algumasseqüên-
ciasdeteclastambémpodemgerarsinais.
Atabela 1mostraossinaismaisimpor-
tantesparamonitorarmos:
Alémdesses,existeofamigeradosi-
nal-9ouSIGKILLque,paraoprocesso
queoestárecebendo,equivaleameter
odedonobotãodedesligardocompu-
tador–oqueéaltamenteindesejável,
jáquemuitosprogramasnecessitam
"limparaárea"aoseutérmino.Seseu
encerramentoocorrerdeformaprevista,
ouseja,setiverumtérminonormal,é
muitofácilfazeressalimpeza;porém,
seoseuprogramativerumfimbrusco,
muitacoisaruimpodeocorrer:
PÉpossívelqueemumdeterminadoes-
paçodetempo,oseucomputadoresteja
cheiodearquivosdetrabalhoinúteis
PSeuprocessadorpoderáficaratolado
deprocessoszombiesedefunctsgera-
dosporprocessosfilhosqueperderam
ospaiseestão“órfãos”;
PÉnecessárioliberarsocketsabertospara
nãodeixarosclientescongelados;
PSeusbancosdedadospoderãoficar
corrompidosporquesistemasgerencia-
doresdebancosdedadosnecessitam
deumtempoparagravarseusbuffers
emdisco(commit).
Enfim,existemmilrazõesparanãousar
umkillcomosinal-9eparamonitoraro
encerramentoanormaldeprogramas.
Listagem 3: Nova versão do fazmenu.sh
01 #!/bin/bash
02 #
03 # Lista enumerando os programas com e tensão .sh no
04 # diretório corrente; e ecuta o escolhido pelo operador
05 #
06 clear; i=1
07 printf "%11s\t%s\n\n" Opção Programa
08 CASE='case $opt in'
09 for arq in *.sh
10 do
11 printf "\t%03d\t%s\n" $i $arq
12 CASE="$CASE
13 "$ printf "%03d \t %s;;" $i $arq
14 i=$ i+1
15 done
16 printf "\t%d\t%s\n\n" 999 "Fim do programa" # Linha incluída
17 CASE="$CASE
18 999 e it;; # Linha alterada
19 * ./erro;;
20 esac"
21 read -n3 -p "Informe a opção desejada: " opt
22 echo
23 e al "$CASE"
LinuxUser
88
PapodeBotequim
www.linuxmagazine.com.br
julho2005 edição10
O comando trapParafazeramonitoraçãodesinaisexiste
ocomandotrap,cujasintaxepodeser
umadasmostradasaseguir:
trap "cmd1; cmd2; cmdn" S1 S2 … SN
trap 'cmd1; cmd2; cmdn' S1 S2 … SN
Ondeoscomandoscmd1, cmd2, cmdn
serãoexecutadoscasooprogramareceba
ossinaisS1, S2…S).Asaspas(")ou
asapóstrofes(')sósãonecessáriascaso
otrappossuamaisdeumcomandocmd
associado.Cadaumadelaspodesertam-
bémumafunçãointerna,umaexterna
ououtroscript.
Paraentenderousodeaspas(")e
apóstrofes (') vamos recorrer a um
exemploque trataum fragmentode
umscriptquefazumatransferênciade
arquivosviaFTPparaumamáquina
remota($RemoComp),naqualousuário
é$Fulano,suasenhaé$Segredoeo
arquivoaserenviadoé$Arq.Suponha
aindaqueessasquatrovariáveisforam
recebidasporumarotinaanteriorde
leituraequeessescriptsejamuitousado
pordiversaspessoas.Vejamosotrecho
decódigoaseguir:
ftp -ivn $RemoComp << FimFTP >> /tmp/$$ U
2>> /tmp/$$
user $Fulano $Segredo
binar
get $Arq
FimFTP
Reparequetantoassaídasdosdiálo-
gosdoFTPcomooserrosencontrados
estãosendoredirecionadospara/tmp/$$,
oqueéumaconstruçãobastantecomum
paraarquivostemporáriosusadosem
scriptscommaisdeumusuário,porque
$$éavariávelquecontémonúmerodo
processo(PID),queéúnico.Comesse
tipodeconstruçãoevita-sequedoisou
maisusuáriosdisputemaposseeosdi-
reitossobreumarquivo.
Casoatransferênciasejainterrompida
porumkillouum[CTRL]+[C],certa-
mentedeixarálixonodisco.Éexatamen-
teessaaformamaiscomumdeusodo
comandotrap.Comoissoétrechode
umscriptdevemos,logonoiníciodele,
digitarocomando:
trap "rm -f /tmp/$$ ; e it" 0 1 2 3 15
Dessaforma,casohouvesseumainter-
rupçãobrusca(sinais1,2,3ou15)antes
doprogramaencerrar(noe itdentrodo
comandotrap),ouumfimnormal(sinal
0),oarquivo/tmp/$$seriaremovido.
Casonãohouvesseainstruçãoe it
nalinhadecomandodotrap,aofinal
daexecuçãodessalinhaofluxodopro-
gramaretornariaaopontoemqueestava
quandorecebeuosinalqueoriginoua
execuçãodessetrap.
NotetambémqueoShellpesquisaa
linhadecomandoumavezquandootrap
éinterpretado(eéporissoqueéusual
colocá-lonoiníciodoprograma)enova-
mentequandoumdossinaislistadosé
recebido.Então,noúltimoexemplo,ova-
lorde$$serásubstituídonomomentoem
queocomandotrapélidopelaprimeira
vez,jáqueasaspas(")nãoprotegemo
cifrão($)dainterpretaçãodoShell.
Sevocêquisessefazerasubstituição
somenteaoreceberosinal,ocomando
deveriasercolocadoentreapóstrofes(').
Assim,naprimeirainterpretaçãodotrap,
oShellnãoveriaocifrão($),asapóstro-
fes(')seriamremovidase,finalmente,o
Shellpoderiasubstituirovalordavariá-
vel.Nessecaso,alinhaficariaassim:
trap 'rm -f /tmp/$$ ; e it' 0 1 2 3 15
Suponhadoiscasos:você temdois
scriptsquechamaremosdescript1,cuja
primeiralinhaseráumtrap,escript2,
colocadoemexecuçãoporscript1.Por
seremdoisprocessosdiferentes,terão
doisPIDsdistintos.
1º Caso:Ocomandoftpencontra-se
emscript1.Nessecaso,oargumentodo
comandotrapdeveriavirentreaspas(")
porque,casoocorresseumainterrupção
([CTRL]+[C]ou[CTRL]+[\])noscript2,
alinhasóseriainterpretadanessemo-
mentoeoPIDdoscript2seriadiferente
doencontradoem/tmp/$$(nãoesqueça
que$$éavariávelquecontémoPIDdo
processoativo);
2º Caso:Ocomandoftpencontra-se
emscript2.Nessecaso,oargumento
docomandotrapdeveriaestarentre
apóstrofes('),poiscasoainterrupção
sedesseduranteaexecuçãodescript1,
oarquivonãoteriasidocriado;casoela
ocorresseduranteaexecuçãodescript2,
ovalorde$$seriaoPIDdesseprocesso,
quecoincidiriacomode/tmp/$$.
Ocomandotrap,quandoexecutado
semargumentos,listaossinaisqueestão
sendomonitoradosnoambiente,bem
comoalinhadecomandoqueseráexe-
cutadaquandotaissinaisforemrecebidos.
Sealinhadecomandosdotrapfornula
(vazia),issosignificaqueossinaisespe-
cificadosdevemserignoradosquando
recebidos.Porexemplo,ocomandotrap
"" 2especificaqueosinaldeinterrup-
ção([CTRL]+[C])deveserignorado.No
últimoexemplo,notequeoprimeiroar-
gumentodeveserespecificadoparaqueo
sinalsejaignoradoenãoéequivalentea
escrevertrap 2,cujafinalidadeéretornar
osinal2aoseuestadopadrão. ➟
Tabela 1: Principais sinais
Código Nome Gerado por:
0 EXIT Fim normal do programa
1 SIGHUP Quando o programa recebe um kill -HUP
2 SIGINT Interrupção pelo teclado. ([CTRL]+[C])
3 SIGQUIT Interrupção pelo teclado ([CTRL]+[\])
15 SIGTERM Quando o programa recebe um kill ou kill -TER(
www.linuxmagazine.com.br
julho2005 edição10 89
PapodeBotequim LinuxUser
Sevocêignorarumsinal,todosossub-
shellsirãoignorá-lo.Portanto,sevocê
especificarqualaçãodevesertomada
quandoreceberumsinal,todosossub-
shellsirãotomaramesmaaçãoquando
receberemessesinal.Ouseja,ossinais
sãoautomaticamenteexportados.Parao
sinalmostrado(sinal2),issosignificaque
ossub-shellsserãoencerrados.Suponha
quevocêexecuteocomandotrap "" 2e
entãoexecuteumsub-shell,quetornaráa
executaroutroscriptcomoumsub-shell.
Seforgeradoumsinaldeinterrupção,
estenãoteráefeitonemsobreoShell
principalnemsobreossub-shellporele
chamados,jáquetodoselesignorarão
osinal.
Emkornshell(ksh)nãoexisteaopção
-sdocomandoreadparalerumasenha.
Oquecostumamosfazeréusarusaro
comandostt comaopção-echo,que
inibeaescritanatelaatéqueseencon-
treumstt echopararestauraressa
escrita.Então,seestivéssemosusandoo
interpretadorksh,aleituradasenhateria
queserfeitadaseguinteforma:
echo -n "Senha: "
stt -echo
read Senha
stt echo
Oproblemacomessetipodeconstru-
çãoéque,casoooperadornãosoubes-
seasenha,eleprovavelmenteteclaria
[CTRL]+[C]ouum[CTRL]+[\]durante
ainstruçãoreadparadescontinuaropro-
gramae,casoagissedessaforma,oseu
terminalestariasemecho.Paraevitarque
issoaconteça,omelhorafazeré:
echo -n "Senha: "
trap "stt echo
e it" 2 3
stt -echo
read Senha
stt echo
trap 2 3
Paraterminaresseassunto,abraum
consolegráficoeescrevanopromptde
comandooseguinte:
$ trap "echo Mudou o tamanho da janela" 28
Emseguida,pegueomouseearraste-o
deformaavariarotamanhodajanela
corrente.Surpreso?ÉoShellorientado
aeventos…Maisunzinho,porquenão
consigoresistir.Escrevaisto:
$ trap "echo já era" 17
Emseguidadigite:
$ sleep 3 &
Vocêacaboudecriarumsub-shellque
irádormirdurante trêssegundosem
background.Aofimdessetempo,você
receberáamensagem“jáera”,porqueo
sinal17éemitidoacadavezemqueum
sub-shellterminaasuaexecução.Para
devolveressessinaisaoseucomporta-
mentopadrão,digite:trap 17 28.
Muitolegalessecomando,né?Sevocê
descobriralgummaterialbacanasobre
usodesinais,porfavormeinformepor
email,porqueémuitoraraaliteratura
sobreoassunto.
Comando getoptsOcomandogetoptsrecuperaasopçõese
seusargumentosdeumalistadeparâ-
metrosdeacordocomasintaxePOSIX.2,
istoé,letras(ounúmeros)apósumsinal
demenos(-)seguidasounãodeum
argumento;nocasodesomenteletras
(ounúmeros),elaspodemseragrupa-
das.Vocêdeveusaressecomandopara
"fatiar"opçõeseargumentospassados
paraoseuscript.
Asintaxeégetopts cadeiadeopcoes
nome.Acadeiadeopcoesdeveexplicitar
umacadeiadecaracterescomtodasas
opçõesreconhecidaspeloscript;assim,
seelereconheceasopções-a -be–c,
cadeiadeopcoesdeveserabc.Sevocê
desejarqueumaopçãosejaseguidapor
umargumento,ponhaumsinaldedois
pontos(:)depoisdaletra,comoema:bc.
Issodizaogetoptsqueaopção-atema
forma-a argumento.Normalmenteum
oumaisespaçosembrancoseparamo
parâmetrodaopção;noentanto,getopts
tambémmanipulaparâmetrosquevêm
coladosàopçãocomoem-aargumento.
cadeiadeopcoesnãopodeconterumsi-
naldeinterrogação(?).
Onomeconstantedalinhadesintaxe
acimadefineumavariávelquereceberá,
acadavezqueocomandogetoptsfor
executado,opróximodosparâmetros
posicionaiseocolocaránavariávelnome.
getoptscolocaumainterrogação(?)na
variáveldefinidaemnomeseacharuma
opçãonãodefinidaemcadeiadeopcoes
ousenãoacharoargumentoesperado
paraumadeterminadaopção.
Comojásabemos,cadaopçãopassada
porumalinhadecomandostemumín-
dicenumérico;assim,aprimeiraopção
estarácontidaem$1,asegundaem$2e
assimpordiante.Quandoogetoptsobtém
umaopção,elearmazenaoíndicedo
próximoparâmetroaserprocessadona
variávelOPTI)D.
Quandoumaopçãotemumargumento
associado(indicadopelo:nacadeiade-
opcoes),getoptsarmazenaoargumento
navariávelOPTARG.Seumaopçãonão
possuirargumentoouseoargumento
esperadonãoforencontrado,avariável
OPTARGserá"apagada"(comunset).Oco-
mandoencerrasuaexecuçãoquando:
PEncontraumparâmetroquenãocome-
çacomumhífen(-).
POparâmetroespecial--indicaofim
dasopções.
PQuandoencontraumerro(porexemplo,
umaopçãonãoreconhecida).
Oexemplodalistagem 4émeramente
didático,servindoparamostrar,emum
pequenofragmentodecódigo,ousople-
nodocomando.
LinuxUser
90
PapodeBotequim
www.linuxmagazine.com.br
julho2005 edição10
Paraentendermelhor,vamosexecutaroscript:
$ getoptst.sh -h -Pimpressora arq1 arq2
getopts fez a variavel OPT_LETRA igual a 'h'
OPTARG eh ''
getopts fez a variavel OPT_LETRA igual a 'P'
OPTARG eh 'impressora'
Dispensando os primeiros $OPTIND-1 = 2 argumentos
O que sobrou da linha de comandos foi 'arq1 arq2'
Dessaforma,semtermuitotrabalho,separei
todasasopçõescomseusrespectivosargumentos,
deixandosomenteosparâmetrosqueforampassa-
dospelooperadorparaposteriortratamento.Repare
que,setivéssemosescritoalinhadecomandocom
oargumento(impressora)separadodaopção(-P),
oresultadoseriaexatamenteomesmo,excetopelo
OPTI)D,jáquenessecasoeleidentificaumconjun-
todetrêsopções(ouargumentos)e,noanterior,
somentedois.Vejasó:
$ getoptst.sh -h -P impressora arq1 arq2
getopts fez a variavel OPT_LETRA igual a 'h'
OPTARG eh ''
getopts fez a variavel OPT_LETRA igual a 'P'
OPTARG eh 'impressora'
Dispensando os primeiros $OPTIND-1 = 3 argumentos
O que sobrou da linha de comandos foi 'arq1 arq2'
Repare,noexemploaseguir,quesepassarmosumaopçãoinválidaa
variável$OPT_LETRAreceberáumpontodeinterrogação(?)ea$OPTARG
será"apagada"(unset).
$ getoptst.sh -f -Pimpressora arq1 arq2 # A opção –f não é valida
./getoptst.sh: illegal option -- f
getopts fez a variavel OPT_LETRA igual a '?'
OPTARG eh ''
getopts fez a variavel OPT_LETRA igual a 'P'
OPTARG eh 'impressora'
Dispensando os primeiros $OPTIND-1 = 2 argumentos
O que sobrou da linha de comandos foi 'arq1 arq2'
–Medizumacoisa:vocênãopoderiaterusadoumcondicionalcom
caseparaevitarogetopts?
–Poderiasim,masparaquê?Oscomandosestãoaíparaseremusados…
Oexemplofoididático,masimagineumprogramaqueaceitassemuitas
opçõesecujosparâmetrospoderiamounãoestarcoladosàsopções,sen-
doqueasopçõestambémpoderiamounãoestarcoladas:iaserumcase
infernal!Comgetopts,ésóseguirospassosacima.
–É…Vendodessaforma,achoquevocêtemrazão.Éporqueeujáestou
meiocansadocomtantainformaçãonovanaminha
cabeça.Vamostomarasaideiraouvocêaindaquer
explicaralgumaparticularidadedoShell?
–Nemumnemoutro,eutambémjácanseimas
hojenãovoutomarasaideiraporqueestouindo
daraulanaUniRIO,queéaprimeirauniversidade
federalqueestápreparandoseusalunosdocurso
degraduaçãoemInformáticaparaousodeSoft-
wareLivre.Masantesvoutedeixarumproblema
parateencucar:quandovocêvariaotamanhode
umajaneladoterminal,nocentrodelanãoaparece
dinamicamente,emvídeoreverso,aquantidadede
linhasecolunas?Então!Euqueroquevocêrepro-
duzaissousandoalinguagemShell.Chico,traz
rapidinhoaminhaconta!Voucontaratéumese
vocênãotrouxereumemando!
Nãoseesqueça,qualquerdúvidaoufaltadecompa-
nhiaparaumchopeésómandarumemailparajulio.
[email protected]émparamandar
omeujabá:digaparaosamigosquequemestivera
fimdefazerumcursoporretadeprogramaçãoem
Shellquemandeume-mailparajulio.neves@tecnohall.
com.brparainformar-se.Valeu! ■
Listagem 4: getoptst.sh
01 $ cat getoptst.sh
02 #!/bin/sh
03
04 # E ecute assim:
05 #
06 # getoptst.sh -h -Pimpressora arq1 arq2
07 #
08 # e note que as informações de todas as opções são e ibidas
09 #
10 # A cadeia 'P:h' diz que a opção -P é uma opção comple a
11 # e requer um argumento e que h é uma opção simples que não requer
12 # argumentos.
13
14 hile getopts 'P:h' OPT_LETRA
15 do
16 echo "getopts fez a aria el OPT_LETRA igual a '$OPT_LETRA'"
17 echo " OPTARG eh '$OPTARG'"
18 done
19 used_up=`e pr $OPTI)D – 1`
20 echo "Dispensando os primeiros \$OPTI)D-1 = $used_up argumentos"
21 shift $used_up
22 echo "O que sobrou da linha de comandos foi '$*'"
www.linuxmagazine.com.br
julho2005 edição10 91
PapodeBotequim LinuxUser
E aírapaz,tudobom?
–Beleza.Vocêselembradequedaúltimavezvocême
pediuparafazerumprogramaqueimprimissedinami-
camente,nocentrodatela,aquantidadedelinhasecolunas
deumterminalsemprequeotamanhodajanelavariasse?
Poisé,euatéquefiz,masmesmodepoisdequebrarmuitoa
cabeçaaaparêncianãoficouigual.
–Nãoestounemaíparaaaparência,oqueeuqueriaéque
vocêexercitasseoqueaprendemos.Medáa listagem 1 praeu
veroquevocêfez.
–Perfeito!Quesedaneaaparência,depoisvouteensinar
unsmacetesparamelhorá-la.Oquevaleéqueoprogramaestá
funcionandoeestábemenxuto.
–Pôxa,eeuqueperdiomaiortempotentandodescobrir
comoaumentarafonte…
–Deixeissoparalá!Hojevamosverumascoisas
bastanteinteressanteseúteis.
Dando nomes aos canos Umoutrotipodepipeéonamedpipe,quetambém
échamadodeFIFO.FIFOéumacrônimodeFirstIn
FirstOutqueserefereàpropriedadeemqueaordem
dosbytesentrandonopipeéamesmaqueadasaída.
Onameemnamedpipeé,naverdade,onomedeum
arquivo.Osarquivostiponamedpipessãoexibidos
pelocomandolscomoqualqueroutro,compoucas
diferenças,veja:
$ ls -l pipe1
pr -r-r-- 1 julio dipao 0 Jan 22 23:11 pipe1|
o p nacolunamaisàesquerdaindicaque fifo1
éumnamedpipe.Orestodosbitsdecontrolede
permissões,quempodelerougravaropipe,funcio-
namcomoumarquivonormal.Nossistemasmais
modernosumabarravertical( | ),oupipe,nofimdo
nomedoarquivoéoutradicae,nossistemasLINUX,
ondeolspodeexibircores,onomedoarquivoé
escritoemvermelhoporpadrão.
A conversa está boa, mas uma hora eles tem que sair do bar. Na última
parte do nosso papo, falamos sobre pipes e sincronização entre processos.
porJúlioCezarNeves
Curso de Shell Script
PapodebotequimPartefinal
Da
ve
Ha
milt
on
- ww
w.s
xc
.hu
Listagem 1: tamtela.sh
01 #!/bin/bash
02 #
03 # Coloca no centro da tela, em ideo re erso,
04 # a quantidade de colunas e linhas
05 # quando o tamanho da tela eh alterado.
06 #
07 trap (uda 28 # 28 = sinal gerado pela mudanca no tamanho
08 # da tela e (uda eh a funcao que fara isso.
09
10 Bold=$ tput bold # (odo de enfase
11 Re =$ tput re # (odo de ideo re erso
12 )orm=$ tput sgr0 # Restaura a tela ao padrao default
13
14 (uda
15 {
16 clear
17 Cols=$ tput cols
18 Lins=$ tput lines
19 tput cup $ $Lins / 2 $ Cols - 7 / 2 # Centro da tela
20 echo $Bold$Re $Cols X $Lins$)orm
21 }
22
23 clear
24 read -n1 -p "(ude o tamanho da tela ou tecle algo para terminar "
LinuxUser
86
PapodeBotequim
www.linuxmagazine.com.br
agosto2005 edição11
Nossistemasmaisantigos,osnamedpipessãocriadospelo
utilitáriomknod,normalmentesituadonodiretório/etc.Nos
sistemasmaismodernos,amesmatarefaéfeitapelomkfifo,
querecebeumoumaisnomescomoargumentoecriapipes
comessesnomes.Porexemplo,paracriarumnamedpipecom
onomepipe1,digite:
$ mkfifo pipe1
Comosempre,amelhorformademostrarcomoalgofunciona
édandoexemplos.Suponhaquenóstenhamoscriadoonamed
pipemostradoanteriormente.Vamosagoratrabalharcomduas
sessõesoudoisconsolesvirtuais.Emumdelesdigite:
$ ls -l > pipe1
eemoutrofaça:
$ cat < pipe1
Voilà!Asaídadocomandoexecutadonoprimeiroconsole
foiexibidanosegundo.Notequeaordememqueoscomandos
ocorreramnãoimporta.
Sevocêprestouatenção,reparouqueoprimeirocomando
executadopareciater"pendurado".Istoaconteceporqueaoutra
pontadopipeaindanãoestavaconectada,eentãoosistema
operacionalsuspendeuoprimeiroprocessoatéqueosegundo
"abrisse"opipe.Paraqueumprocessoqueusapipenãofique
emmododeespera,énecessárioqueemumapontadopipe
hajaumprocesso"falante"enaoutraum"ouvinte".Noexemplo
anterior,olserao"falante"eocaterao"ouvinte".
Umusomuitoútildosnamedpipesépermitirqueprogramas
semnenhumarelaçãopossamsecomunicarentresi.Osnamed
pipestambémsãousadosparasincronizarprocessos,jáque
emumdeterminadopontovocêpodecolocarumprocessopara
"ouvir"oupara"falar"emumdeterminadonamedpipeeeledaí
sósairáseoutroprocesso"falar"ou"ouvir"aquelepipe.
Vocêjádeveternotadoqueessaferramentaéótimapara
sincronizarprocessosefazerbloqueioemarquivosdeformaa
evitarperda/corrupçãodedadosdevidoaatualizaçõessimul-
tâneas(afamosaconcorrência).Vamosveralgunsexemplos
parailustrarestescasos.
Sincronização de processosSuponhaquevocêdispareparalelamentedoisprogramas(pro-
cessos),chamadosprograma1eprograma2,cujosdiagramasde
blocosdesuasrotinassãocomomostradonatabela 1.Osdois
processossãodisparadosemparaleloe,nobloco1doprograma1,
astrêsclassificaçõessãodisparadasdaseguintemaneira:
for Arq in BigFile1 BigFile2 BigFile3
do
if sort $Arq
then
(anda= a
else
(anda=pare
break
fi
done
echo $(anda > pipe1
[ $(anda = pare ] &&
{
echo Erro durante a classificação dos arqui os
e it 1
}
…
Assimsendo,ocomandoiftestacadaclassificaçãoqueestá
sendoefetuada.Casoocorraqualquerproblema,asclassificações
seguintesserãoabortadas,umamensagemcontendoastring
pareéenviadapelopipe1eprograma1édescontinuadocom
códigodesaídasinalizandoumencerramentoanormal.
Enquantooprograma1executavaoseuprimeirobloco(as
classificações),oprograma2executavaoseubloco1,proces-
sandoassuasrotinasdeaberturaemenuparalelamenteao
programa1,ganhandodessaformaumbomtempo.Ofragmento
decódigodoprograma2aseguirmostraatransiçãodoseu
bloco1paraobloco2:
OK=`cat pipe1`
if [ $OK = a ]
then
…
Rotina de impressão
Tabela 1
Programa1 Programa2
Bloco 1 Rotina de classificação de três grandes arquivos Rotina de abertura e geração de menus
Bloco 2 Acertos finais e encerramento Impressão dos dados classificados pelo programa 1
www.linuxmagazine.com.br
agosto2005 edição11 87
PapodeBotequim LinuxUser
…
else
e it 1
fi
Apósaexecuçãodeseuprimeirobloco,oprograma2passará
a"ouvir"opipe1,ficandoparadoatéqueasclassificaçõesdo
Programa1terminem,testandoaseguiramensagempassada
pelopipe1paradecidirseosarquivosestãoíntegrosparaserem
impressosouseoprogramadeveráserdescontinuado.Dessa
formaépossíveldispararprogramasdeformaassíncronae
sincronizá-losquandonecessário,ganhandobastantetempo
deprocessamento.
Bloqueio de arquivosSuponhaquevocêtenhaescritoumCGI(CommonGatewayInter-
face)emShellScriptparacontarquantoshitsumadeterminada
URLrecebeearotinadecontagemestádaseguintemaneira:
Hits="$ cat page.hits 2> /de /null " || Hits=0
echo $ Hits=Hits++ > page.hits
Dessaforma,seapáginareceberdoisoumaisacessossimul-
tâneos,umoumaispoderáserperdido,bastandoqueosegundo
acessosejafeitoapósaleituradoarquivopage.hitseantes
dasuagravação,istoé,apósoprimeiroacessoterexecutadoa
primeiralinhadoscripteantesdeexecutarasegunda.
Então,oquefazer?Pararesolveroproblemadeconcor-
rência,vamosutilizarumnamedpipe.Criamososcriptna
listagem 2queseráodaemonquereceberátodosospedidos
paraincrementarocontador.Notequeelevaiserusadopor
qualquerpáginanonossositequeprecisedeumcontador.
Comoapenasestescriptalteraosarquivos,nãoexisteopro-
blemadeconcorrência.
Estescriptseráumdaemon,istoé,rodaráemsegundoplano.
Quandoumapáginasofrerumacesso,elaescreveráasuaURL
nopipe.Paratestar,executeestecomando:
echo "teste_pagina.html" > /tmp/pipe_contador
Paraevitarerros,emcadapáginaaquequisermosadicionar
ocontadoracrescentamosaseguintelinha:
<!--#e ec cmd="echo $REQUEST_URI > /tmp/pipe_contador"-->
Notequeavariável$REQUEST_URIcontémonomedoarquivo
queobrowserrequisitou.Esseexemploéfrutodeumatroca
deidéiascomoamigoemestreemShellThobiasSalazarTre-
visan,queescreveuoscriptecolocou-oemseuexcelentesite
(www.thobias.org).Aconselhoatodososquequeremaprender
Shelladarumapassadalá.
Vocêpensaqueoassuntonamedpipesestáesgotado?Enga-
nou-se.Voumostrarumusodiferenteapartirdeagora.
Substituição de processosVoumostrarqueoShelltambémusaosnamed
pipesdeumamaneirabastantesingular,queéa
substituiçãodeprocessos(processsubstitution).Uma
substituiçãodeprocessosocorrequandovocêpõe
um<ouum>grudadonafrentedoparênteseda
esquerda.Digitarocomando:
$ cat < ls -l
Resultaránocomandols -lexecutadoemum
sub-shell,comonormal,porémredirecionaráasaída
paraumnamedpipetemporário,queoShellcria,
nomeiaedepoisremove.Entãoocatteráumnome
dearquivoválidoparaler(queseráestenamedpipe
ecujodispositivológicoassociadoé/de /fd/63).
Oresultadoéamesmasaídaqueageradapelo
ls -l,porémdandoumoumaispassosqueousual.
Praquesimplificar?
Comopoderemosnoscertificardisso?Fácil…Veja
ocomandoaseguir:
Listagem 2: contahits.sh
01 #!/bin/bash
02
03 PIPE="/tmp/pipe_contador" # arqui o named pipe
04 # dir onde serao colocados os arqui os contadores de cada pagina
05 DIR="/ ar/ /contador"
06
07 [ -p "$PIPE" ] || mkfifo "$PIPE"
08
09 hile :
10 do
11 for URL in $ cat < $PIPE
12 do
13 FILE="$DIR/$ echo $URL | sed 's,.*/,,' "
14 # quando rodar como daemon comente a pro ima linha
15 echo "arqui o = $FILE"
15
17 n="$ cat $FILE 2> /de /null " || n=0
18 echo $ n=n+1 > "$FILE"
19 done
20 done
LinuxUser
88
PapodeBotequim
www.linuxmagazine.com.br
agosto2005 edição11
$ ls -l > cat
l- –– 1 jne es jne es 64 Aug 27 12:26 /de /fd/63 -> pipe:[7050]
É…Realmenteéumnamedpipe.Vocêdeveestarpensando
queistoéumamaluquicedenerd,né?Entãosuponhaquevocê
tenhadoisdiretórios,chamadosdiredir.bkp,edesejasaber
seosdoissãoiguais.Bastacompararoconteúdodosdiretórios
comocomandocmp:
$ cmp < cat dir/* < cat dir.bkp/* || echo backup furado
ou,melhorainda:
$ cmp < cat dir/* < cat dir.bkp/* >/de /null || echo backup furado
Esteéumexemplomeramentedidático,massãotantosos
comandosqueproduzemmaisdeumalinhadesaídaqueele
servecomoguiaparaoutros.Euquerogerarumalistagemdos
meusarquivos,numerando-os,eaofinalmostrarototalde
arquivosnodiretóriocorrente:
hile read arq
do
i++ # assim nao eh necessario inicializar i
echo "$i: $arq"
done < < ls
echo ")o diretorio corrente `p d` e istem $i arqui os"
Tálegal,euseiqueexistemoutrasformasdeexecutara
mesmatarefa.Usandoocomandowhile,aformamaiscomum
deresolveresseproblemaseria:
ls | hile read arq
do
i++ # assim nao eh necessario inicializar i
echo "$i: $arq"
done
echo ")o diretorio corrente `p d` e istem $i arqui os"
Aoexecutaroscript,tudopareceestarbem,porémnocoman-
doechoapósodone,vocêveráqueovalorde$ifoiperdido.
Issodeve-seaofatodestavariávelestarsendoincrementadaem
umsub-shellcriadopelopipe(|)equeterminounocomando
done,levandocomeletodasasvariáveiscriadasnoseuinterior
easalteraçõesláfeitasporvariáveiscriadasexternamente.
Somenteparatemostrarqueumavariávelcriadaforado
sub-shellealteradaemseuinteriorperdeasalteraçõesfeitas
quandoosub-shellseencerra,executeoscriptaseguir:
#!/bin/bash
LIST="" # Criada no shell principal
ls | hile read FILE # Inicio do subshell
do
LIST="$FILE $LIST" # Alterada dentro do subshell
done # Fim do subshell
echo $LIST
Noiníciodesteexemploeudissequeeleerameramente
didáticoporqueexistemformasmelhoresdefazeramesma
tarefa.Vejasóestasduas:
$ ls | ln
ouentão,usandoaprópriasubstituiçãodeprocessos:
$ cat -n < ls
Umúltimoexemplo:vocêdesejacomparararq1earq2usando
ocomandocomm,masessecomandonecessitaqueosarquivos
estejamclassificados.Entãoamelhorformadeprocederé:
$ comm < sort arq1 < sort arq2
Essaformaevitaquevocêfaçaasseguintesoperações:
$ sort arq1 > /tmp/sort1
$ sort arq2 > /tmp/sort2
$ comm /tmp/sort1 /tmp/sort2
$ rm -f /tmp/sort1 /tmp/sort2
Pessoal,onossopapodebotequimchegouaofim.Curtimuito
erecebidiversoselogiospelotrabalhodesenvolvidoaolongo
dedozemesese,omelhordetudo,fizmuitasamizadesetomei
muitoschopesdegraçacomosleitoresqueencontreipelos
congressosepalestrasqueandofazendopelonossoquerido
Brasil.Medespeçodetodosmandandoumgrandeabraçoaos
barbudosebeijosàsmeninaseagradecendoosmaisde100
emailsquerecebi,todoselogiososedevidamenterespondidos.
Àsaúdedetodosnós:Tim,Tim.
-Chico,fechaaminhacontaporquevoupracasa! ■
Sob
re
oa
uto
r Julio Cezar Neves é Analista de Suporte de Sistemas desde 1969 e trabalha com Unix desde 1980, quando participou do desenvolvi-mento do SOX, um sistema operacional similar ao Unix produzido pela Cobra Computadores. É autor do livro Programação Shell
Linux, publicado pela editora Brasport. Pode ser contatado no e-mail [email protected]
www.linuxmagazine.com.br
agosto2005 edição11 89
PapodeBotequim LinuxUser