55
O ambiente Linux Para você entender o que é e como fun- ciona o Shell, primeiro vou te mostrar como funciona o ambiente em camadas do Linux. Dê uma olhada no gráfico mostrado na Figura 1. Neste gráfico podemos ver que a ca- mada de hardware é a mais profunda e é formada pelos componentes físicos do seu computador. Em torno dela, vem a camada do kernel que é o cerne do Linux, seu núcleo, e é quem põe o hard- ware para funcionar, fazendo seu geren- ciamento e controle. Os programas e comandos que envolvem o kernel, dele se utilizam para realizar as tarefas para que foram desenvolvidos. Fechando tudo isso 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, de forma que tudo que interage com o sistema operacional, tem que passar pelo seu crivo. O ambiente Shell Bom já que para chegar ao núcleo do Linux, no seu ker- nel que é o que interessa a todo aplicativo, é necessária a filtragem do Shell, vamos enten- der como ele funciona de forma a tirar o máximo proveito das inú- meras facilidades que ele nos oferece. O Linux, por definição, é um sistema multiusuário – não podemos nunca nos esquecer disto – e para permitir o acesso de determinados usuários e barrar a en- trada de outros, existe um arquivo cha- mado /etc/passwd, que além de fornecer dados para esta função de “leão-de-chá- cara” do Linux, também provê informa- ções para o início de uma sessão (ou “login”, para os íntimos) daqueles que passaram por esta primeira barreira. O último campo de seus registros informa ao sistema qual é o Shell que a pessoa vai receber ao iniciar sua sessão. Lembra que eu te falei de Shell, fa- mília, irmão? Pois é, vamos começar a entender isto: o Shell é a conceituação de concha envolvendo o sistema opera- cional propriamente dito, é o nome genérico para tratar os filhos desta idéia que, ao longo dos muitos anos de exis- D iálogo entreouvido em uma mesa de um botequim, entre um usuá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ê precisa reiniciar dez vezes por dia e ainda por cima não tem domínio nenhum sobre o que esta acontecendo no seu com- putador. Mas deixa isso prá lá, pois vou te explicar o que é Shell e os com- ponentes de sua família e ao final da nossa conversa você dirá: “Meu Deus do Shell! Porque eu não optei pelo Linux antes?”. 82 Agosto 2004 www.linuxmagazine.com.br Papo de Botequim LINUX 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 Stephen Bourne do Bell Labs (da AT&T,onde também foi desenvolvido o Unix), foi durante muitos anos o Shell padrão do sistema operacional Unix. É também chamado de Standard Shell por ter sido durante vários anos o único, e é até hoje o mais utilizado. Foi portado para praticamente todos os ambientes Unix e dis- tribuições Linux. Korn Shell (ksh): Desenvolvido por David Korn, 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 trazendo muitos usuários e programadores de Shell para 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 seus adeptos é o que mais cresce em todo o mundo, seja por que ele é o Shell padrão do Linux, seja por sua grande diversidade de comandos, que incorpora inclusive diversos comandos característicos do C Shell. C Shell (csh): Desenvolvido por Bill Joy, da Universidade de Berkley, é o Shell mais uti- lizado em ambientes BSD. Foi ele quem intro- duziu o histórico de comandos. A estruturação de seus comandos é bem simi- lar à da linguagem C. Seu grande pecado foi ignorar a compatibilidade com o sh, partindo por um caminho próprio. Além destes Shells existem outros, mas irei falar somente sobre os três primeiros, tratan- do-os genericamente por Shell e assinalando as especificidades de cada um. Quadro 1: Uma rapidinha nos principais sabores de Shell

Papo de botequim

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Papo de botequim

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

Page 2: Papo de botequim

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

Page 3: Papo de botequim

$ 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

Page 4: Papo de botequim

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

Page 5: Papo de botequim

$ 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

Page 6: Papo de botequim

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

Page 7: Papo de botequim

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

Page 8: Papo de botequim

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”

Page 9: Papo de botequim

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

Page 10: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 11: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 12: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 13: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 14: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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… ■

Page 15: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 16: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 17: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 18: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 19: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 20: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 21: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 22: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 23: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

[email protected]

BR

EO

AU

TO

R

Page 24: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 25: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 26: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

Page 27: Papo de botequim

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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�����������

����� �����

��

��������

��������

����

����� ��������

Page 28: Papo de botequim

���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������

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

[email protected]

BR

EO

AU

TO

R

Page 29: Papo de botequim

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

Page 30: Papo de botequim

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

Page 31: Papo de botequim

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

Page 32: Papo de botequim

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

Page 33: Papo de botequim

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

Page 34: Papo de botequim

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

Page 35: Papo de botequim

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

Page 36: Papo de botequim

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

Page 37: Papo de botequim

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

Page 38: Papo de botequim

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

Page 39: Papo de botequim

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

Page 40: Papo de botequim

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

Page 41: Papo de botequim

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

Page 42: Papo de botequim

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

Page 43: Papo de botequim

$ 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

Page 44: Papo de botequim

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

Page 45: Papo de botequim

$ 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.

[email protected]

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

Page 46: Papo de botequim

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

Page 47: Papo de botequim

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

Page 48: Papo de botequim

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

Page 49: Papo de botequim

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

Page 50: Papo de botequim

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

Page 51: Papo de botequim

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

Page 52: Papo de botequim

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

Page 53: Papo de botequim

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

Page 54: Papo de botequim

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

Page 55: Papo de botequim

$ 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