Sei sulla pagina 1di 28

Curso de Shell - Aula I

Por: Alex Borro ( 25/12/2000 )

Introduo
O que so os shell scripts? Shell scripts so conjuntos de comandos armazenados
em um arquivo texto que so executados seqencialmente. Nesta primeira parte,
faremos uma introduo sobre o shell, o formato desses arquivos e variveis de
ambiente.

O que Shell
Shell, ou interpretador de comandos, o programa disparado logo aps o login
responsvel por "pegar" os comandos do usurio, interpret-los e executar uma
determinada ao.
Por exemplo, quando voc escreve no console "ls" e pressiona <enter>, o shell l
essa string e verifica se existe algum comando interno (embutido no prprio shell)
com esse nome. Se houver, ele executa esse comando interno. Caso contrrio, ele
vai procurar no PATH por algum programa que tenha esse nome. Se encontrar, ele
executa esse programa, caso contrrio, ele retorna uma mensagem de erro. Para
cada terminal ou console aberto, existe um shell sendo executado. Quando voc
entra no seu Linux, ele apresenta o login, para digitar o usurio e a senha. Ao
digitar um par usurio/senha correto, o Linux abre automaticamente um shell, que
vai ficar esperando seus comandos. Veja o exemplo abaixo:
Welcome to Linux Slackware 7.1 kernel 2.2.16.
matrix login: neo
Password:
Linux 2.2.16.
Last login: Mon Sep 25 10:41:12 -0300 2000 on tty1.
No mail.
neo@matrix:~$
Essa ultima linha o shell. Se voc der o comando "ps", vai ver que um dos
programas rodando o seu shell:
neo@matrix:~$ ps
PID TTY TIME CMD
164 tty2 00:00:00 bash
213 tty2 00:00:00 ps
neo@matrix:~$
Ou seja, o shell utilizado nesse console o bash, que est rodando com PID 164.
Existem diversos tipos de shell: bash, csh, ksh, ash, etc. O mais utilizado
atualmente o bash (GNU Bourne-Again SHell). Por isso, tomaremos ele como
referncia.
Resumindo essa seo, importante saber que para cada terminal ou console
aberto, tem-se um shell rodando. Assim, se voc tem 3 xterms abertos na interface
grfica, vai ter um shell para cada xterm.
Ao executar o shell script, o shell atual (no qual voc deu o comando) abre um novo
shell para executar o script. Assim, os scripts so executados em um shell prprio (a
menos que se especifique, ao chamar o script, para execut-lo no shell atual). Isso
ser importante quando formos tratar de variveis de ambiente.

Variveis do ambiente
Uma varivel onde o shell armazena determinados valores para utilizao
posterior.
Toda varivel possui um nome e um valor associado a ela, podendo ser este ltimo
vazio. Para listar as variveis atualmente definidas no shell digite o comando set .
Para se definir uma varivel, basta utilizar a sntaxe: nome_da_varivel=valor .
Por exemplo, queremos definir uma varivel chamada "cor" com o valor de "azul":
neo@matrix:~$ cor=azul
Para utilizar o valor de uma varivel, s colocar um sinal de "$" seguido do nome
da varivel - o shell automaticamente substitui pelo valor da varivel:
neo@matrix:~$ echo cor
cor
neo@matrix:~$ echo $cor
azul
Em alguns casos, aconselhvel colocar o nome da varivel entre chaves ({}). Por
exemplo, se eu quero imprimir "azul-escuro", como faria? Simplesmente echo
$cor-escuro ?? No funcionaria, pois o bash vai procurar uma varivel de nome
"cor-escuro". Portanto, temos que colocar o nome "cor" entre chaves para delimitar
o nome da varivel:
neo@matrix:~$ echo ${cor}-escuro
azul-escuro
Algumas variveis j so predefinidas no shell, como o PATH, que, como foi dito
antes, armazena o caminho dos programas. Por exemplo, a minha varivel PATH
contm:
neo@matrix:~$ echo $PATH
/usr/local/bin:/usr/bin: /bin: /usr/X11R6/bin: /usr/openwin/bin:
/usr/games: /opt/kde/bin: /usr/share/texmf/bin: /etc/script
Ou seja, quando digito um comando, como "ls", o shell vai comear a procur-lo
em /usr/local/bin, se no encontr-lo, vai procurar em /usr/bin e assim por diante.
Repare que os diretrios so separados por um sinal de dois pontos (:).
importante destacar que o shell possui vrias variveis pr-definidas, ou seja, que
possuem um significado especial para ele, entre elas: PATH, PWD, PS1, PS2, USER e
UID.
Assim, quando iniciamos um novo shell (ao executar o nosso script), essas variveis
especiais so "herdadas" do shell pai (o que executou o shell filho). Outras variveis
definidas pelo usurio, como a varivel "cor" no so passadas do shell pai para o
filho.
Quando o script terminar, o seu shell (shell filho) simplesmente desaparece e com
ele tambm as suas variveis, liberando o espao ocupado na memria.

Formato dos arquivos de Shell Script


A primeira linha de todo shell script deve comear com algo do tipo: #!/bin/bash ,
a qual indica com qual shell dever ser executado o script.Nesse exemplo, estamos
falando para o shell atual executar o script com o shell /bin/bash.Se quisermos que
o nosso script seja executado com o shell csh, devemos colocar nessa primeira
linha: #!/bin/csh .
Como usaremos o bash como nosso shell de referncia, todas as linhas dos nossos

scripts comearo com #!/bin/bash


Digamos que voc executa freqentemente o comando: find / -name file -print ,
que procura na raiz (/) por um arquivo de nome "file". S que chato ficar digitando
esse comando toda vez que se quer procurar um arquivo.
Ento vamos criar um shell script que contenha esse comando. Vamos chamar esse
shell script de "procura". Seu contedo fica assim:
#!/bin/bash
find / -name file -print
Pronto. Tornemos agora o arquivo executvel: chmod 755 ./procura . Porm, ao
tentar executar o nosso script, teremos um problema.
./procura
Este script ir procurar por um arquivo chamado "file".Como especificar qual arquivo
queremos procurar? Seria ideal executarmos o nosso script seguido do nome do
arquivo que queremos procurar.
Por exemplo, queremos saber onde est o arquivo "netscape": ./procura netscape
. ai que entram as "variveis de parmetro". Vamos substituir no nosso script a
linha find / -name file -print por find / -name $1 -print .
Quando o bash l a varivel "$1", ele a substitui pelo primeiro parmetro passado
na linha de comando para o nosso script. Ento, se executamos ./procura
netscape , a varivel "$1" ser substituda por "netscape", como a gente queria.
Repare que a varivel "$2" conteria o segundo parmetro passado para o script e
assim por diante.
Sendo assim, qualquer comando colocado abaixo de find seria executado aps ele.
Esse o essencial do shell script: poder automatizar a execuo de programas e
comandos como se estivessem sendo digitados diretamente no console ou terminal.

Concluso
Na prxima aula do nosso curso de shell script iremos aprender alguns comandos
especiais para tornar nossos script mais poderosos, fazendo coisas mais elaboradas
do que apenas executar programas seqencialmente. Entre eles, podemos destacar
os comandos de lao, como "if", "for", "while", "case" etc.

Curso de Shell - Aula II


Por: Alex Borro ( 26/12/2000 )

Introduo
Na aula de hoje vamos falar sobre execuo de programas em primeiro plano (fg foreground) e em segundo plano (bg - background), redirecionamento de sadas e
tambm sobre os cdigos de escape dos programas.

Execuo em foreground e background


Quando executamos um programa, o shell fica esperando o mesmo terminar para
depois nos devolver a linha de comando. Por exemplo, quando executamos o
comando cp arquivo1 arquivo2,o shell executa esse comando, fica esperando ele
terminar, para depois nos retornar a linha de comando. Isso chama-se execuo em
primeiro plano, ou foreground, em ingls.Agora digamos que o arquivo a ser copiado
seja muito grande, digamos, 50Mb. Enquanto o comando cp executado, vamos
ficar com o shell preso a ele todo esse tempo, ou seja, cerca de 1 ou 2 minutos.

Somente aps isso vamos ter a linha de comando de volta para podermos continuar
trabalhando.
E se pudssemos dizer ao shell para executar um programa e nos retornar a linha de
comando sem ficar esperando o tal programa terminar? Seria muito melhor, no? Em
alguns casos seria. E podemos fazer isso.
Uma maneira fcil colocar o sinal de & depois de um comando. No exemplo acima
ficaria:
neo@matrix:~$ cp arquivo1 arquivo2 &
[1] 206
neo@matrix:~$
Pronto, assim que teclarmos [enter], teremos a nossa linha de comando de volta e
mais duas informaes: "[1] 206".
A primeira informao ([1]) chama-se job. Ela identifica os programas que esto
sendo executados em bg (background). Por exemplo, se executarmos mais um
programa em bg enquanto este "cp" est sendo executado, o novo programa recebe
o job de nmero 2 ([2]) e assim por diante.
A segunda informao (206) o pid do programa, ou seja, o nmero de processo
que o kernel d a esse programa.
Mas a temos um pequeno problema. Digamos que voc queira trazer o programa
para fg, por exemplo, se o programa executado em bg for um tocador de mp3 e
voc quiser mudar a msica. Como fazemos? Usamos o comando fg [job] (intuitivo,
no ?) do bash. No exemplo acima, digamos que eu execute o "cp" em bg, mas
depois queira traz-lo para fg para cancel-lo. Seria simples:
neo@matrix:~$ cp arquivo1 arquivo2 &
[1] 206
neo@matrix:~$ fg 1
cp arquivo1 arquivo2
Assim trazemos o "cp" para primeiro plano e a linha de comando s retorna quando
ele terminar.
Opcional
Uma outra maneira de colocar um programa para rodar em bg utilizando um
truque do bash (no sei se esta opo est disponvel em outros shells).Digamos que
voc execute o comando "cp" e esquea de colocar o sinal "&". E a? Tem que ficar
esperando acabar ou cancelar o comando? No, podemos teclar: Ctrl + Z.
Ao fazer isso, paramos a execuo do programa. Ento s dar o comando bg [job]
(intuitivo tambm, n?):
neo@matrix:~$ cp arquivo1 arquivo2
* aqui teclamos Ctrl + Z *
[1]+ Stopped cp -i arquivo1 arquivo2
neo@matrix:~$ bg 1
[1]+ cp -i arquivo1 arquivo2 &
neo@matrix:~$
Quando teclamos o Ctrl + Z, o bash nos diz que o job 1 foi parado ([1]+ Stopped) e
quando executamos "bg 1" ele nos diz que o comando voltou a ser executado, mas
em bg (repare o sinal de "&" no final da linha): [1]+ cp -i arquivo1 arquivo2 &.

Redirecionamento de sadas
Muitos programas que executamos geram sadas no console, ou sejam, emitem

mensagens para os usurios. Por exemplo, queremos achar quantos arquivos no


nosso sistema tem "netscape" no nome:
neo@matrix:~$ find / -name netscape
/usr/lib/netscape
/usr/lib/netscape/netscape
/usr/lib/netscape/nethelp/netscape
/usr/local/bin/netscape
Agora se quisermos procurar quantos arquivos compactados com o gzip (extenso
.gz) tem no nosso sistema para depois analis-los e possivelmente apagar os
repetidos ou inteis, teremos uma lista muito grande. Aqui no meu sistema deu mais
de 1000 arquivos. Ento, seria til podermos enviar as mensagens que vo para o
console, para um arquivo. Assim, poderamos analis-lo depois de executar o
comando. Isso extremamente til ao trabalharmos com shell script - enviar as
sadas dos comandos para arquivos para depois analis-las. Exemplificando:
neo@matrix:~$ find / -name "*.gz" > lista.txt
find: /home/vera: Permisso negada
find: /home/pri: Permisso negada
find: /root: Permisso negada
neo@matrix:~$
Notamos que nem tudo foi para o arquivo lista.txt. As mensagens de erro foram para
o console.
Isso porque existem dois tipos de sada: a sada padro e a sada de erro.A sada
padro a sada normal dos programas, que no caso acima, seria os arquivos
encontrados. E a sada de erro, como o prprio nome diz, so os erro encontrados
pelo programa durante sua execuo, que devem ser informados ao usurio, como
os erros de "permisso negada".
E a, como selecionar uma ou outra? simples, apenas devemos indicar o "file
descriptor" (fd) delas. No vamos entrar em detalhes sobre o que "descritor de
arquivos" por fugir do nosso escopo e ser um pouco complicado, apenas temos que
saber que o fd 1 corresponde a sada padro e o fd 2 a sada de erro. Assim, no
exemplo acima, para enviar os erro para o arquivo erros.txt e a sada padro para o
arquivo lista.txt, usaramos:
neo@matrix:~$ find / -name "*.gz" 1> lista.txt 2> erros.txt
Ou seja, s por o nmero da sada desejada antes do sinal de ">".
Agora digamos que queremos ver o contedo do arquivo lista.txt. Podemos abr-lo
com um editor de textos ou usar o "cat". Optando pela ltima opo:
neo@matrix:~$ cat lista.txt
/home/neo/gkrellm-0.10.5.tar.gz
/home/neo/linuxcall-interface-beta.tar.gz
...
<<< mais de 1000 linhas
neo@matrix:~$
Temos um problema. O arquivo tem mais de 1000 linhas e no conseguimos v-lo
inteiro. Podemos redirecionar a sada do comando "cat" para o comando "more", que
d pausa entre as telas:
neo@matrix:~$ cat lista.txt | more
.........
Veja que utilizamos o caractere "|", chamado "pipe". No poderamos ter utilizado o
sinal de ">"? No, o sinal de ">" somente para enviar a sada para um ARQUIVO.

Para enviar a sada de um comando para a entrada de outro, usamos o pipe.


As vezes queremos que alm da sada ir para um arquivo, ela tambm v para a
tela. Temos um programinha que faz isso. Ele chama-se "tee". Veja o exemplo a
seguir:
neo@matrix:~$ find / -name "*.gz" 2> erros.txt | tee lista.txt
O que fizemos???Primeiro pegamos a sada de erro e enviamos para o arquivo
erros.txt. O restante, ou seja, a sada padro, estamos enviando para a entrada do
comando tee (usando o pipe). O tee simplesmente pega o que ele recebe (atravs
do pipe) e joga no arquivo lista.txt e na tela. Assim, alm de gravarmos a sada num
arquivo, podemos mostrar ao usurio o que est acontecendo. Isso muito til
quando escrevemos alguns scripts.
Resumindo: aprendemos que podemos redirecionar a sada padro ou de erro de
programa para um arquivo usando respectivamente "1>" ou "2>" ou tambm enviar
a sada para a entrada de outro programa usando o pipe "|".

Cdigos de Escape
Toda vez que executamos um programa em Unix, ele retorna um cdigo de escape
ao finalizar. Esse cdigo reflete a condio em que o programa finalizou. Se ocorreu
tudo certo e o programa terminou normalmente, ele retorna 0. Se ocorreu algum
problema, o programa retorna um cdigo diferente de 0, geralmente variando com o
problema ocorrido.
Esse cdigo de retorno extremamente importante em shell script, pois assim que
testamos se uma certa ao ocorreu bem ou teve problemas.Esse cdigo
armazenado pelo bash numa varivel chamada "?" (isso mesmo, somente o sinal de
interrogao ;-)).
Por exemplo, vamos executar um "ls" em um diretrio que existe e ver o cdigo de
retorno:
neo@matrix:~$ ls /boot
System.map boot.0300 boot.b boot_message.txt chain.b config map os2_d.b
neo@matrix:~$ echo $? 0
neo@matrix:~$
Ou seja, o "ls" foi executado normalmente, retornando 0. Agora vamos execut-lo
num diretrio que no existe:
neo@matrix:~$ ls /diretorio_invalido
/bin/ls: /diretorio_invalido: Arquivo ou diretrio no encontrado
neo@matrix:~$ echo $?
1
neo@matrix:~$
Como esperado, obtemos o retorno de erro 1.
Alguns programas tem muitos cdigos de retorno. Por exemplo, os cdigos de
retorno do "pppd" vo at o 19. Assim possvel saber porque ele foi finalizado. Se
voc colocar uma senha errada no pppd e tentar conectar, ele vai terminar com o
cdigo 19.
man pppd
...
17 The PPP negotiation failed because serial loopback was detected.
18 The init script failed (returned a non-zero exit status).

19 We failed to authenticate ourselves to the peer.


...
Um detalhe importante: quando executamos um programa em background, ele
sempre retorna um cdigo 0 para o shell, mesmo que durante sua execuo ocorra
algum problema. Assim, quando executamos um programa em bg, perdemos essa
facilidade de testar como foi seu trmino.
neo@matrix:~$ ls /diretorio_invalido &
[1] 230
neo@matrix:~$ /bin/ls: /diretorio_invalido: Arquivo ou diretrio no encontrado
[1]+ Exit 1 /bin/ls $LS_OPTIONS /diretorio_invalido
neo@matrix:~$ echo $?
0
Como vemos, ao terminar, ele emite uma mensagem dizendo que finalizou com
cdigo 1 ([1]+ Exit 1) mas quando testamos a varivel "?", o bash nos diz "0".

Concluso
Como todos os programas tem que terminar com um cdigo de retorno que tenha
algum significado, nossos shell scripts tambm tero que finalizar indicando o que
aconteceu, se ocorreu tudo bem ou se ouve erro. Mas isso discutiremos melhor mais
pra frente.
O importante aqui saber que todos os programas terminam com um cdigo de
retorno, os quais usaremos nos nossos scripts para testar o trmino dos programas.

Curso de Shell - Aula III


Por: Alex Borro ( 27/03/2001 )

Introduo
Nesta terceira parte do nosso curso de shell script, vamos tratar de
"condicionais".Condicionais so comandos que avaliam uma expresso. Se ela for
verdadeira, uma determinada rotina executada. Caso contrrio, outra rotina pode
ser executada.

O famoso "if"
O bash nos oferece, entre outros, o comando IF (ele um comando embutido no
bash e no um programa como o "ls", o "cp" etc.). A forma mais simples de
representar uma condicional utilizando o IF, da seguinte forma bsica:
if [condio];
then comandos1;
else comandos2;
fi;
Se [condio] for verdadeira, os comandos1 so executados. Se for falsa, os
comandos2 so executados.Mas para o IF, o que uma condio verdadeira ou uma
falsa?
Como foi explicado na aula anterior, TODO comando em Unix tem um cdigo de
retorno. Geralmente o cdigo "0" (zero) significa que o comando foi executado
perfeitamente e terminou bem. Cdigos maiores que zero significam que alguma

coisa de errado ocorreu.


assim que o IF verifica uma condio. Se o seu cdigo de retorno for zero, ento
ela considerada verdadeira. Caso contrario, ela falsa.Mas ento a [condio] tem
que ser um comando, certo? Exatamente. Vamos exemplificar:
neo@matrix:~$ if ls /boot; then echo "O diretrio existe."; else echo
"Diretrio invlido."; fi;
System.map boot.0300 boot.b boot_message.txt chain.b config map
os2_d.b
O diretrio existe.
O que fizemos?
Logo aps o if, ns colocamos um comando: "ls /boot". O que o IF faz? Ele executa
esse comando (por isso que temos a segunda linha comeada por "System.map",
que o contedo do meu diretrio /boot) e avalia o seu cdigo de sada. Como o "ls"
foi executado corretamente, ele retorna zero, significando verdadeiro para o IF, que
executa o comando logo aps o "then", ou seja, o echo "O diretrio existe.",
mostrando essa mensagem no console.
Agora vamos colocar um diretrio que no existe:
neo@matrix:~$ if ls /dir_invalido; then echo "O diretrio existe."; else echo
"Diretrio invlido."; fi;
/bin/ls: /dir_invalido: Arquivo ou diretrio no encontrado
Diretrio invlido.
A lgica a mesma. Executa o "ls /dir_invalido", que retorna um cdigo maior que
zero. O IF avalia como falso e executa o comando aps o else: echo "Diretrio
invlido".
Ns poderamos omitir a segunda linha dos dois exemplo (a que mostra o contedo
de /boot no primeiro exemplo e a mensagem de erro emitida pelo ls dizendo que
/dir_invalido no existe no segundo), apenas redirecionando as sadas para
/dev/null, ou seja:
neo@matrix:~$ ls /boot 1> /dev/null 2> /dev/null
Nota: Tem um macete que possui o mesmo efeito. Em vez de colocar: "1> /dev/null
2> /dev/null" podemos colocar "2&>1", que menor e mais simples.

O comando "test"
Bom, aprendemos que o IF avalia a cdigo de retorno de um comando. Mas muitas
vezes, para no dizer a maioria, ns queremos avaliar "expresses", ou seja,
verificar se uma varivel igual a outra, se ela esta vazia etc.
Para isso, ns temos outro comando chamado "test" (intuitivo o nome, no?). Ele
funciona da seguinte maneira: test [expresso].
O test pode testar operaes de trs tipos: strings, arquivos e aritmticas.
Expresses usando strings:
O test pode apenas comparar strings, ou seja, verificar se uma igual a outra, e
verificar se uma string vazia ou no. Vamos aos exemplos para facilitar o
entendimento:
neo@matrix:~$ test "a" = "a"
neo@matrix:~$ echo $?

0
neo@matrix:~$
neo@matrix:~$
1
neo@matrix:~$
neo@matrix:~$
0

test "a" = "b"


echo $?
test "a" != "b"
echo $?

Aqui comparamos a string "a" com "b". Como era de se esperar, o primeiro retornou
verdadeiro (zero), pois a = a e o segundo retornou falso. No terceiro, o smbolo "!="
significa "diferente".
neo@matrix:~$
neo@matrix:~$
1
neo@matrix:~$
neo@matrix:~$
0
neo@matrix:~$
neo@matrix:~$
0
neo@matrix:~$
neo@matrix:~$
1

test -z "neo"
echo $?
test -z ""
echo $?
test -n "neo"
echo $?
test -n ""
echo $?

Acima temos os testes de vazio. A opo "-z" verifica se vazio, e "-n" se no


vazio. No primeiro caso, ele testa se "neo" uma string vazia, retornando falso. J
no segundo caso, como "" vazia, retorna verdadeiro. O terceiro e quarto so
semelhantes aos primeiros, mas com "-n".
Expresses com arquivos:
Os testes que podem ser feitos com arquivos so para verificar determinadas
caracteristicas, como se ele existe, se executavel, se um link simblico, se um
diretrio etc.
Alguns exemplos:
A opo "-e" verifica apenas se um arquivo existe e a opo "-d" verifica se o
arquivo um diretrio.
A opo "-nt" verifica se o primeiro arquivo mais novo que o segundo (nt = newer
than) e "-ot" verifica se o primeiro mais velho que o segundo (od = older than):
neo@matrix:~$
neo@matrix:~$
0
neo@matrix:~$
neo@matrix:~$
1
neo@matrix:~$
neo@matrix:~$
0
neo@matrix:~$
neo@matrix:~$
0
neo@matrix:~$
neo@matrix:~$
0

test -e /vmlinuz
echo $?
test -d /vmlinuz
echo $?
test -e /usr
echo $?
test -d /usr
echo $?
test /usr -nt /vmlinuz
echo $?

neo@matrix:~$ test /usr -ot /vmlinuz


neo@matrix:~$ echo $?
1

O comando "test" (continuao)


A seguir, temos uma lista de vrias opes disponveis:

-b arquivo - Verdadeiro se arquivo um arquivo de bloco, como /dev/hda.

-c arquivo - Verdadeiro se arquivo um arquivo de caracter, como /dev/tty1.

-d arquivo - Verdadeiro se arquivo um diretrio.

-e arquivo - Verdadeiro se arquivo existe.

-f arquivo - Verdadeiro se arquivo existe e um arquivo comum.

-s arquivo - Verdadeiro se arquivo existe e no vazio.

-h arquivo - Verdadeiro se arquivo um link simblico.

-p arquivo - Verdadeiro se arquivo um "named pipe" (fifo, lifo, etc).

-S arquivo - Verdadeiro se arquivo um "socket".

-k arquivo - Verdadeiro se arquivo tem seu "sticky bit" ligado.

-r arquivo - Verdadeiro se arquivo pode ser lido pelo usurio atual.

-w arquivo - Verdadeiro se arquivo pode ser escrito pelo usurio atual.

-x arquivo - Verdadeiro se arquivo pode ser executado pelo usurio atual.

-O arquivo - Verdadeiro se arquivo pertence ao usurio atual.

-G arquivo - Verdadeiro se arquivo pertence ao grupo do usurio atual.

-N arquivo - Verdadeiro se arquivo foi modificado desde a ultima vez que foi lido.

Expresses Aritmticas
Expresses aritmticas consistem com comparar dois nmeros, verificando se so
iguais, ou se o primeiro maior que o segundo etc.
Infelizmente no podemos apenas utilizar os smbolos conhecidos para igual (=),
maior que (>), menor que (<) etc. Temos que usar operadores reconhecidos pelo
"test". Assim, no podemos fazer: "test 1 = 1", devemos utilizar o operador "-eq"
(equal): "test 1 -eq 1". A seguir, temos uma lista dos operadores:

-eq (equal): Igual;

-ne (not-equal): No Igual (diferente);

-lt (less than): Menor que (<);

-le (less than or equal): Menor ou igual ( <= );

-gt (greater than): Maior que (>);

-ge (greater than or equal): Maior ou igual (>=);

Alguns exemplos:

neo@matrix:~$
neo@matrix:~$
0
neo@matrix:~$
neo@matrix:~$
1
neo@matrix:~$
neo@matrix:~$
0
neo@matrix:~$
neo@matrix:~$
0

test 1 -lt 2
echo $?
test 1 -gt 2
echo $?
test 2 -gt 1
echo $?
test 2 -ge 2
echo $?

Para finalizar, vamos fazer duas consideraes. A primeira de que o comando "test"
pode ser substituido por um par de colchetes [ ]. Assim, o comando test "a" = "b"
pode ser escrito como [ "a" = "b" ] .
Essa segunda nomenclatura fica mais fcil e simples, principalmente quando
estamos utilizando IF:
if [ -e /vmlinuz ]; mais intuitivo e simples que if test -e /vmlinuz; .
A segunda considerao que, obviamente, podemos utilizar variveis no lugar dos
argumentos (caso contrrio, ficaria difcil utilizar comandos de condicionais em shell
script). Assim, se tivermos duas variaveis, valor1=5 e valor2=8:
[ "$valor1" = "$valor2" ] , [ "$valor1" -lt "$valor2" ] etc.
importante colocarmos os valores entre aspas duplas ("), pois assim o bash pode
substituir o que se encontra dentro dessas aspas. Se colocarmos entre aspas simples
('), impedimos o bash de alterar o que se encontra dentro delas. Se no utilizarmos
as aspas duplas, vamos ter problemas, principalmente ao trabalharmos com string.
Por exemplo, queremos comparar dois nomes, que se encontram em duas
variaveis:nome1="fulano de tal" e nome2="ciclano".Montando nossa expresso
condicional: [ "$nome1" = "$nome2" ].O bash ir expandir isso para: [ "fulano de
tal" = "ciclano" ], ficando claramente definidos o primeiro e o segundo argumento.
Agora, se no usarmos aspas: [ $nome1 = $nome2 ]. O bash ir expandir isso para:
[ fulano de tal = ciclano ]. Perceba que os argumentos se misturam e o bash no vai
saber o que comparar.Por isso importante colocarmos os argumentos entre aspas
duplas.

Concluso
Resumindo, aprendemos na aula de hoje como utilizar comandos condicionais. Isso
ser fundamental nas proximas aulas, onde iremos tratar os comandos de lao,
como o while. No perca!

Curso de Shell - Aula IV


Por: Alex Borro ( 10/04/2001 )

Nesta aula vamos aprender sobre comandos de lao como o while, for, case e select.
Eles nos permitem executar alguns comandos diversas vezes, sob determinadas
condies e at montar menuzinhos para interagir com o usurio.

While
Este provavelmente o comando de lao mais utilizado em programao. Seu
entendimento simples. Ele testa uma condio (como faz o IF) e executa um
conjunto de comandos se esta condio for verdadeira. Aps a execuo desses
comandos, ele torna a testar a condio e se esta for verdadeira, ele reexecuta os
comandos e assim por diante.
Sua sintaxe a seguinte:
while [ condio ];
do
comando 1;
comando 2;
...
done;

O parmetro [ condio ] funciona exatamente igual ao do IF. No voltarei a abortar


os parmetros condicionais pois eles j foram explicados na aula 3. Em caso de
dvida sobre isso, uma rpida reviso na aula 3 suficiente.
Bom, vamos ao exemplo mais simples do While: um contador.
x=0
While [ "$x" -le 10 ];
do
echo "Execuo nmero: $x"?;
x = $((x+1));
done;

Analisando:
Na primeira linha temos a condio: enquanto o valor da varivel x ( $x ) for menor-

igual ( -le ) a 10, faa: mostre "Execuo nmero: " e o valor de x ($x). Faa x = x
+ 1. Isso no bash feito pelo comando $(( )). Ele realiza operaes algbricas com
variveis. No caso acima, estamos somando x + 1 e colocando o resultado no
prprio x.
Preste ateno, que as linhas do While precisam de um ";" para terminar, exceto a
que contm o "do", pois ele representa o comeo do bloco de comandos.
Quando executamos o script acima, temos uma contagem de 1 at 10:
neo@matrix:~$ x=0; while [ "$x" -lt 10 ]; do x=$((x+1)); echo
"Execuo nmero: $x"; done;
Execuo nmero: 1
Execuo nmero: 2
...
Execuo nmero: 10
neo@matrix:~$

O While tem muita utilidade em programao, mas fica difcil dar exemplos usando
somente ele, pois geralmente ele est associado a execuo de outros programas e
rotinas mais complexas.
Eu costumo usar ele, por exemplo, quando quero que o napster fique tentando
conectar, pois quando ele vai tentar conectar o servidor e no consegue, ele
simplesmente termina. Ai eu coloco o while testando a cdigo de retorno dele
(lembram da varivel $? ) e enquanto estiver diferente de zero (o napster terminou
com erro), ele fica executando o napster:
? = 1; while [ "$?" -ne "0" ]; do ./nap; done;

O comando "? = 1" tenta atribuir 1 a varivel ?. Isso provoca um erro, pois a
varivel ? somente de leitura. Isso necessrio para que ? Contenha algo diferente
de 0, pois seno o While no executa a primeira interao.

For
O for semelhante ao while usado como um contador. necessrio fornecer uma
lista de nomes e ele executa os comandos para cada nome na lista. Parece meio
confuso, mas simples. Veja a sintaxe:
for <var> in <lista>;
do
comandos
done;

O For associa o primeiro item da lista de nomes varivel <var> e executa os


comandos. Em seguida, associa novamente o segundo item da lista <var> e
executa novamente os comandos... e assim por diante, at acabar a lista.
Veja o exemplo:
neo@matrix:~$ for x in Compra Venda Aluguel; do echo $x; done;
Compra
Venda
Aluguel

Ou seja, primeiro ele coloca "Compra" na varivel x e executa os comandos, no caso,


"echo $x", e assim por diante.
Podemos facilmente implementar um contador, usando em conjunto com o for, o
programinha "seq". Ele simplesmente gera uma seqncia de nmeros. Por exemplo,

"seq 10" gera uma seqncia de 1 at 10. Assim, podemos us-lo no for:
for x in $(seq 10); do echo $x; done;

Esse comando semelhante ao contador implementado com o While. Primeiro o x


vale 1 e o for executa os comandos. Depois x vale 2 e ele reexecuta os comandos...
Vamos usar o For para renomear os arquivo de um diretrio, mudando todos os
arquivo terminados em ".mp3" para ".mp3.bak".
for x in *; do
mv "$x" "${x}.bak";
done;
for x in *.mp3; do
if [ -e "$x" ];
then mv "$x" "${x}.bak";
fi;
done;

No local de <lista> nos colocamos "*.mp3". Isso diz ao bash para expandir
(transformar) ele na lista de arquivos terminados com ".mp3". Seno houver
nenhum arquivo com essa terminao, o bash no faz nada, ficando para o for a
string "*.mp3". Por isso precisamos do IF para testar se o arquivo existe.
Experimente no seu sistema.
echo *.mp3

Vai mostrar os arquivos no diretrio atual com terminao mp3, mas sem quebra de
linha entre eles, ou seja, um nome aps o outro. Se no houver nenhum arquivo
com terminao mp3, ele apenas vai mostrar "*.mp3".
Bom, voltando ao For, ele vai atribuir a "x" cada nome na lista de arquivos com
terminao ".mp3" e executar os comandos seguintes.
Primeiro ele testa para ver se o arquivo existe ( if [ -e "$x" ]; ), e se existir,
renomeia ele para o seu prprio nome acrescido de ".bak" ( ${x}.bak ).
Resumindo, o For faz isso: voc fornece uma lista de nomes para ele e ele vai
atribuindo esses nomes, em ordem e um por vez, varivel <var> e executa os
comandos entre o "do" e o "done;".

Case
O Case est mais para um comando condicional do que para comando de lao, visto
que ele no executa "loopings" como o While e o For.
Ele geralmente utilizado como substituio de vrios IFs.. Um exemplo clssico e
muito utilizado quando voc precisa testar um parmetro fornecido na linha de
comando. O Case utilizado desta forma em scripts de inicializao de servios do
sistema.
Vou mostrar a sintaxe em seguida um script que inicialize/ reinicialize ou pare algum
servio (como o sendmail, apache, bind, etc).
Sintaxe:
case <parmetro> in
<opo 1>)
<comandos 1>
;;

[opo 2] )
*)

<comandos 2>
;;
< comandos se no for nenhuma das

opes >

;;

esac

Vamos as explicaes. O Case pega a string fornecida em <parmetro> e compara


com <opo 1>. Se forem iguais, ele executa <comandos 1> e sai fora. Caso
contrario, ele compara <parmetro> com <opo 2> e assim por diante.
Caso <parmetro> no seja igual a nenhuma das opes, ele executa os comandos
da opo "*", se este existir.
Prestem ateno a alguns detalhes na sintaxe. Deve existir um ")" aps cada opo
e tambm um ";;" aps todos os comandos de cada opo. Vejam o exemplo abaixo:
case "$1" in
'start' )

'restart' )

'stop' )

*)

echo "Iniciando o servio..."


<comandos para iniciar o servio>
;;
echo "Reinicializando o servio..."
<comandos para reinicializar o servio>
;;
echo "Parando o servio..."
<comandos para parar o servio>
;;

echo "Opo invalida!"


echo "As opes vlidas so:
start, stop e restart"
;;
esac

O Case serve exatamente para isso, ou seja, evitar o teste de vrios Ifs. No caso
acima, teramos que utilizar 3 Ifs. Mesmo se o primeiro j fosse verdadeiro, o bash
iria testar o segundo e o terceiro, ou seja, ia perder tempo desnecessariamente. J
no case isso no acontece. Aps entrar em uma opo e executar seus comandos,
ele j pula fora do case sem testar as outras opes abaixo.

Select
O Select um comando de lao que nos permite mostrar um pequeno menuzinho de
opes para o usurio. Cada opo possui um nmero e para escolher, o usurio
digita o nmero correspondente a opo desejada. Vejamos a sintaxe a seguir:
select <var> in <lista de opes>;
do
<comandos>
done;

Como disse acima, o select vai mostrar as opes contidas em <lista de opes>,
uma por linha, com um nmero na frente e espera que o usurio digite a opo

desejada. Ao digitar a opo, o select atribui o nome da opo a varivel <var> e


executa os comandos. Para sair do select, necessrio executar o comando "break".
Vamos a um exemplo:
select x in Iniciar Reiniciar Parar Sair; do
echo "Opo Escolhida: $x"
if [ "$x" == "Sair" ]; then break; fi;
done;

Ou seja, se o usurio escolher alguma opo diferente de "Sair", o script apenas


escreve a opo. Se for escolhida Sair, ele mostra a opo, entra no IF e executa o
break, saindo do select.
interessante combinar o Select com o Case. Vamos mostrar como ficaria aquele
exemplo do case, de iniciar um servio, utilizando o Select, para tornar o script
interativo:
select x in Iniciar Reiniciar Parar Sair; do
case "$x" in
'Iniciar' )
echo "
servio..."
;;
'Reiniciar' )
echo "
servio..."
;;
'Parar' )
echo "
;;
'Sair' )
echo "
break
;;
*)
echo "
invlida!"
;;
esac
done;

Iniciando o

Reinicializando o

Parando o servio..."
Script encerrado."

Opo

Primeiramente o Select mostra um menuzinho:


1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#?

Quando o usurio digitar um nmero de opo, o select executa o case e este


compara a varivel x com suas opes. Se achar alguma, executa seus comandos,
no caso um echo. Se o usurio escolher Sair, alm do echo, ele executa o "break",
que interrompe o select:
neo@matrix:~/test$ ./t
1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#? 1
Iniciando o servio...

1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#? 5
1) Iniciar
2) Reiniciar
3) Parar
4) Sair
#? 4

Opo invlida!

Script encerrado.
neo@matrix:~/test$

Bom pessoal, acho que por hoje s. ;-) Espero que vocs tenham pego a idia de
como funciona comandos de laos. Pelos menos sabendo a utilidade, sintaxe e como
funcionam, quando surgir a necessidade, vocs j vo saber quais ferramentas usar.

Curso de Shell - Aula V


Por: Alex Borro ( 24/04/2001 )

Nesta aula teremos um breve tutorial sobre o Grep. Ele no somente um dos
comandos mais teis, mas o seu domnio abre portas para dominar outros poderosos
comandos, como o sed (que trataremos na prxima aula) , awk, perl, etc.
Eu fiz uma adaptao/ modificao de um tutorial que eu tenho, e por sinal
excelente, sobre o Grep e Expresses Regulares em ingls. Espero que tenha ficado
legal e vocs gostem.

O que ele faz?


O grep basicamente faz buscas. Mais precisamente:
grep palavra file retorna todas as linhas do arquivo file
que contenham palavra

Outro jeito de usar o grep atraves de pipe (lembram dos pipes?). Por exemplo:
ls | grep palavra

Lista todos os arquivos que contenham palavra em seu nome. Ou seja, a entrada do

grep uma lista de arquivos (gerada pelo ls) que ser filtrada, sendo impressas
somente as linhas que contenham palavra.

Usando caracteres coringas


Suponho que todos saibam o que so caracteres coringas. Caso contrrio, coringas
so caracteres especiais que substituem outros. Geralmente o caracter "*" um
coringa que significa "qualquer caracter em qualquer quantidade". Por isso se a
gente executar "ls *", onde "*" entra no lugar do "nome do arquivo", significando
qualquer string de qualquer tamanho. Por isso ele lista todos os arquivos.
Mas agora voltemos ao grep. Ser que ele aceita coringas ??? A resposta mais do
que sim. O grep suporta algo que vai alm de coringas, ele suporta Expresses
Regulares. Mas vamos comear apenas com coringas. Um dos mais usados com o
grep o "." Vamos a um exemplo:
>cat file
file
big
bad bug
bigger
boogy

>grep b.g
big
bad bug
bigger

Note que boogy no casa, desde que "." significa "qualquer e apenas um caracter".
Para significar strings arbitrrias utilizamos "*", que funciona da seguinte maneira:
"A expresso consistindo de um caracter seguido por um * casa com qualquer
nmero (inclusive zero) de repeties desse caracter. Em particular, ".*" significa
qualquer string."
Para compreendermos vamos a mais exemplos:
>cat file
big
bad bug
bag
bigger
boogy>grep b.*g file
big
bad bug
bag
bigger
boogy>grep b.*g. File
bigger
boogy>grep ggg* file
bigger

Avanando para expresses regulares


Os coringas so o comeo, mas a idia vai mais longe. Por exemplo, suponha que
queremos uma expresso que case com Frederic Smith ou Fred Smith, ou seja, a
string "eric" opcional.
Primeiro, introduzimos o conceito de um "caracter escapado (escaped character)".
"Um caracter escapado um caracter precedido por uma barra invertida ( \ ). Essa
barra invertida faz o seguinte: (a) Remove qualquer significado especial do caracter.

(b) acrescenta um significado especial a um caracter que no tenha um significado


especial."
Parece complicado, mas veja nos exemplo que um tanto quanto simples:
Para procurar uma linha contento o texto "hello.gif", o comando correto seria:
grep 'hello\.gif' file

Desde que "grep 'hello.gif' file" iria resultar linhas contendo: hello-gif , hello1gif ,
helloagif , etc.
Ou seja, a barra invertida remove o significado especial do ".", passando esse a
significar um simples ponto ao invs de um coringa.
Agora vamos passar para expresses agrupadas, a fim de encontrar um jeito de criar
uma expresso que case com Frederic ou Fred. Primeiro vamos comear com o
operador "?":
"Uma expresso consistindo de caracter seguido por uma interrogao escapada ( \?
) casa com zero ou uma instncia daquele caracter".
Exemplo:
"bugg\?y" casa com o seguinte: bugy , buggy mas no com
bugggy
neo@matrix:~$ echo bugy | grep "bugg\?y"
bugy
neo@matrix:~$ echo bugggy | grep "bugg\?y"
neo@matrix:~$

Agora vamos para expresses agrupadas. No nosso exemplo, queremos tornar


opcional a string "eric" aps "Fred", ou seja, no apenas um caracter mas sim um
conjunto de caracteres (string).
"Uma expresso dentro de parnteses escapados tratada como um nico caracter"
Exemplos:
"Fred\(eric\)\?" Smith casa com "Fred Smith" or "Frederic Smith"
\(abc\)* casa com abc , abcabcabc, etc (isto , qualquer
nmero de repeties da string "abc", incluindo
zero).

Note que temos que tomar cuidado quando nossas expresses contm espaos em
branco. Quando eles aparecem, precisamos colocar a expresso entre aspas, para
que o shell no interprete mal nosso comando. Para o exemplo acima:
grep "Fred\(eric\)\? Smith" file

Eu aconselho fortemente a sempre usar aspas, mesmo que no usemos espaos em


brancos. J tive muita dor de cabea porque expresses e comandos no
funcionavam simplesmente por no estarem entre aspas. Um dos exemplo acima (o
do bugg\?y) no funciona no meu sistema se no estiver entre aspas. Veja:
neo@matrix:~$ echo bugy | grep "bugg\?y"
bugy
neo@matrix:~$ echo bugy | grep bugg\?y
neo@matrix:~$

Outros operadores teis


Para casar algum caracter de uma lista, use [ ] Veja:

"[Hh]ello" casa com linhas contendo "hello" ou "Hello"

Faixas de caracteres tambm so permitidos:


[0-3] o mesmo que [0123]
[a-k] o mesmo que [abcdefghijk]
[A-C] o mesmo que [ABC]
[A-Ca-k] o mesmo que [ABCabcdefghijk]

Existem tambm algumas formas alternativas:


[[:alpha:]] o mesmo que [a-zA-Z]
[[:upper:]] o mesmo que [A-Z]
[[:lower:]] o mesmo que [a-z]
[[:digit:]] o mesmo que [0-9]
[[:alnum:]] o mesmo que [0-9a-zA-Z]
[[:space:]] casa com qualquer quantidade de espaos,
inclusive tabulaes

Essas formas alternativas, como [[:digit:]] prefervel ao mtodo direto, [0-9].


Os [ ] podem ser usado para indicar caracteres que NO devem estar na expresso.
s colocar o sinal ^ na primeira posio da lista. Veja:
neo@matrix:~$ echo hello | grep "[Hh]ello"
hello
neo@matrix:~$ echo hello | grep "[^Hh]ello"
neo@matrix

Outro exemplo, um pouco mais complicado:


grep "([^()]*)a" file retorna qualquer linha contendo um par de parentes seguido
por "a" e que NO contenham outros parnteses dentro. Assim, ele casa com essas
linhas:
(hello)a
(aksjdhaksj d ka)a

Mas no com: x=(y+2(x+1))a

Casando com um nmero especifico de repeties


Suponha que voc queira casar um nmero especfico de repeties de uma
expresso. Um bom exemplo so nmeros de telefones. Voc pode procurar por um
nmero de telefone com sete dgitos, assim:
grep "[:digit:]\{3\}[ -]\?[:digit:]\{4\}" file

Ou seja, trs dgitos, opcionalmente um espao ou um hfen e mais 4 dgitos.


Procurando por comeo e fim de linha:
Isso muito interessante. Digamos que voc queira procurar por linhas que
contendo espaos em brancos no comeo da linha, a palavra hello e ento o fim da
linha. Vamos comear com este exempo:
>cat file
hello
hello world
hhello
>grep hello file
hello
hello world
hhello

Isso no o que ns queremos. O que est errado ? O problema que o grep


procura por linhas contendo a string hello e todas as linhas especificadas contem ela.

Para contornar isso, introduzimos os caracteres de comeo e fim de linha:


"O caracter ^ significa comeo de linha e o $ significa fim da linha"
Retornando ao nosso exemplo:
grep "^[[:space:]]*hello[[:space:]]*$" file

Ou seja, o comeo da linha, qualquer quantidade de espao em branco (inclusive


zero), a palavra hello, qualquer quantidade de espao em branco e o fim da linha.
Essa expresso faz o que a gente quer, retornando somente a linha que contm a
palavra hello. Outro exemplo:
grep "^From.*Alex" /var/spool/mail/neo

Procura no meu inbox por mensagens de uma pessoa em particular (no caso, Alex).
Esse tipo de expresso regular extremamente til e filtros de e-mail, como o
procmail, utilizam isso para fazerem tudo.
Isso ou Aquilo: Procurando uma coisa OU outra:
"A expresso consistindo de duas expresses separadas pelo operador OU \| casa
linhas contendo uma das duas expresses"
Note que voc DEVE colocar a expresso dentro de aspas simples ou duplas:
grep "cat\|dog" file casa com linhas contendo a palavra "cat" ou a
palavra "dog"
grep "I am a \(cat\|dog\)" casa com linhas contendo a string "I am
a cat" ou a string "I am a dog".

Usando backreference (referencia anterior)


Digamos que voc queira procurar strings que contenham uma substring em mais de
um lugar. Um exemplo as tags de cabealhos de HTML. Suponha que eu queira
procurar por "<H1>alguma string</H1>". Isto fcil de se fazer. Mas suponha que
eu queira fazer o mesmo, mas permita H2 H3 H4 H5 e H6 no lugar de H1. A
expresso <H[1-6]>.*</H[1-6]> no boa, desde que casa com "<H1>alguma
string</H3>" e nos queremos que a tag de abertura seja igual a de fechamento.
Para fazermos isso, usamos backreference:
"A expresso \n onde n um nmero, casa com o contedo do n-simo conjunto de
parnteses na expresso".
Nossa... isso realmente precisa de um exemplo!!!!
<H\([1-6]\)>.*</H\1> faz o que nos queramos fazer acima...
"O Senhor \(dog\|cat\) e a senhora \1 foram visitar o Senhor \(dog\|cat\) e a
senhora \2"
Esse outro exemplo bobo. Os casais tem sempre que serem iguais. Ou um casal de
cachorro ou de gato.

Alguns detalhes cruciais: caracteres especiais e aspas


Caracteres Especiais:
Aqui ns descrevemos os caracteres especiais para RegExp (expresses regulares)
no grep. Note que no "egrep", que usa expresses regulares estendidas (atualmente
no tem nenhuma funcionalidade a mais que as expresses regulares normais do
GNU grep), a lista de caracteres especiais so os mesmos, diferindo somente que
alguns no precisar estar "escapados".

Os seguintes caracteres so considerados especiais, e precisam estar escapados:


?

Note que o sinal de $ perde seu sentido se tiver caracteres depois dele, do mesmo
jeito que o sinal ^ perde seu sentido se tiver caracteres antes dele. Os colchetes [ ]
comportam-se um pouco diferente. A baixo segue as regras para eles:

O colchete direito ( ] ) perde seu sentido especial se colocado no comeo de uma lista, por
exemplo: "[]12]" casa com ] , 1, or 2.

Um hfen perde seu significado especial se colocado por ltimo. Assim, [15-] casa com 1, 5 ou -

O caracter ^ perde seu sentido se no for colocado em primeiro lugar.

A maioria dos caracteres especiais perdem seu significado especial se forem colocados dentro de
colchetes.

Aspas:
Em primeiro lugar, aspas simples so mais seguras de usar porque elas protegem
suas expresses regulares de serem alteradas pelo bash (como foi visto nas aulas
anteriores). Por exemplo, grep "!" file vai produzir um erro, j que o shell pensa que
"!" est se referindo ao comando de histrico do shell, enquanto grep '!' file funciona
perfeitamente.
Quando voc deve usar aspas simples ? A resposta : se voc precisa usar variveis
do shell, use aspas duplas. Caso contrrio, use aspas simples. Por exemplo:
grep "$HOME" file

Procura em file pelo nome do seu diretrio pessoal, enquanto grep '$HOME' file
procura pela string $HOME.

Sintaxe das expresses regulares estendidas


Agora vamos ver a sintaxe do egrep em contraste com a sintaxe do grep.
Ironicamente, apesar do nome "estendido", o egrep atualmente tem menos
funcionalidade do que quando foi criado, para manter a compatibilidade com o
tradicional grep. A melhor maneira de fazer um grep estendido utilizar grep -E que
usa a sintaxe de expresses regulares estendidas sem perda de funcionalidade.

Curso de Shell - Aula VI


Por: Alex Borro ( 26/12/2000 )

Nesta aula continuaremos nosso tutorial, desta vez falando sobre o Sed. Vale
lembrar que este tutorial dar uma breve introduo ao Sed, ajudando os iniciantes
a entender como ele funciona. Portanto muitos comandos mais complexos sero
omitidos, j que seria necessrio um livro inteiro para ensinar tudo sobre o Sed. Mas
no se preocupem, ensinaremos o suficiente sobre ele para utilizao em shell script.

Resumo de RegExp
Vamos a um resumo sobre as expresses regulares, explicadas na aula anterior e agora aplicadas ao Sed:
^
casa com o comeo de linha
$
casa com o fim de linha
.
casa com qualquer caracter simples
(apenas um)
(caracter)*
casa com qualquer ocorrncia, em qualquer
quantidade, de (caracter)
(caracter)?
casa com zero ou uma ocorrncia de
(caracter)
[abcdef]
Casa com qualquer caracter dentro dos [ ]
(neste caso, a b c d e ou f), faixas de caracteres como [a-z]
so
permitidas.
[^abcdef]
Casa com qualquer caracter NO
includo em [] (neste caso, qualquer caracter que
no seja a b c d e ou f)
(caracter)\{m,n\}
casa com m-n repeties de
(caracter)
(caracter)\{m,\}
casa com m ou mais repeties de
(caracter)
(caracter)\{,n\}
casa com n ou menos (tambm zero)
repeties de (caracter)
(caracter)\{n\}
casa com exatamente n repeties
de (caracter)
\(expresso\)
operador de grupo.
\n
Backreference - casa com o
n-simo grupo
expresso1\|expresso2
Casa com expresso1 ou
expresso2. Funciona com o GNU sed, mas essa
caracterstica pode no funcionar com outros Seds.

Caracteres Especiais
Os caracteres especiais no Sed so os mesmo do Grep, com uma diferena: a barra
normal / um caracter especial no sed. A razo disso ficar clara mais para frente
quando estudarmos os comandos do sed.

Como funciona: Uma breve introduo


O Sed funciona assim: ele l da entrada padro, uma linha de cada vez. Para cada
linha, ele executa uma srie de comandos de edio e ento a linha escrita na
sada padro. Um exemplo que mostra como ele funciona: Ns usamos o comando
"s", que significa "substitute" (substituir) ou "search and replace" (procurar e
trocar). O formato :
s/expresso-regular/texto-substituto/{flags}

Ns no vamos discutir todas as flags ainda. A nica que usamos abaixo a "g", que

significa "substitua todas as ocorrncias":


>cat file
Eu tenho trs cachorros e dois gatos
>sed -e 's/cachorros/gatos/g' -e 's/gatos/elefantes/g' file
Eu tenho trs elefantes e dois elefantes

OK, ento o que aconteceu? Primeiro o sed leu a linha do arquivo "file" e executou:
s/cachorros/gatos/g
que produziu o seguinte texto:
Eu tenho trs gatos e dois gatos
e ento o segundo comando foi executado na linha j
editada e resultou:
Eu tenho trs elefantes e dois elefantes

Ns atualmente damos um nome para o texto (geralmente uma linha) que o sed leu
e est processando (editando): ele chama-se "pattern space" (uma boa traduo
seria "rea de edio").
O sed l da entrada padro e joga na sua rea de edio, executando nela uma
seqncia de comandos de edio e ento ele escreve o resultado na sada padro.

Comandos de Substituio e Deleo


Primeiro, as maneiras mais usuais do sed a seguinte:
>sed -e 'comando1' -e 'comando2' -e 'comando3' arquivo
>{comando shell} | sed -e 'comando1' -e 'comando2'
>sed -f sedscript.sed arquivo
>{comando shell} | sed -f sedscript.sed

Ento, o sed pode ler do arquivo ou da entrada padro, e os comandos podem ser
especificados em um arquivo de script ou na linha de comando. Esse arquivo,
chamado sedscript.sed um arquivo que contm todos os comandos do sed, ao
invs de serem especificados na linha de comando. Esses sed's scripts so teis
quando precisamos de um processamento de texto mais complexo e refinado.
Note o seguinte: se os comandos so lidos de um arquivo (sed script), espaos em
branco podem ser fatais. Eles podem fazer o script falhar sem explicao aparente.
Eu recomendo editar os arquivos de comandos do sed com um editor como o VIM
que pode mostrar o final da linha e voc pode ver se existem espaos em branco
entre os comandos e o fim da linha.

Comando de Substituio
O formato para o comando de substituio o seguinte:
[endereo1[,endereo2]]s/procura/substituto/[flags]

As flags podem ser as seguinte:


n
troca a n-sima ocorrncia (na linha) do
texto "procura" por "substituto"
g
troca todas as ocorrncias (na linha) do
texto "procura" por "substituto"
p
imprime a "rea de edio" para a
sada padro se ocorrer uma substituio com

sucesso
w arquivo imprime a "rea de edio" para arquivo
se ocorrer uma substituio com sucesso

Se nenhuma flag for especificada, somente a primeira ocorrncias na linha


substituda. Note que ns quase sempre usamos o comando "s" ou com a flag "g" ou
sem nenhuma flag.
Se um endereo dado, ento a substituio aplicada a linhas que contenham
aquele endereo. Um endereo pode ser ou uma expresso regular dentro de barras
normais /regexp/ , ou um nmero de linha. O smbolo $ pode ser usado no lugar do
nmero da linha para denotar a ltima linha.
Se dois endereos so fornecidos, separados por uma vrgula, ento a substituio
aplicada a todas as linhas entre duas linhas que casam com os endereos fornecidos.
Isto requer algum esclarecimento. Mais precisamente, a substituio ocorre em
todas as linhas desde a primeira ocorrncia de "endereo1" at a primeira ocorrncia
de "endereo2".
No se preocupe se isso tudo parece meio confuso. Os exemplos vo esclarecer
melhor.

O comando de Deleo
A sintaxe desse comando muito simples. Ai vai:
[endereo1[,endereo2]]d

Isto deleta o contedo da "rea de edio" (se esta casar com os endereos
fornecidos). Todos os comandos seguintes sero pulados (j que no a nada a fazer
com uma rea de edio em branco) e uma nova linha ser lida e jogada na rea de
edio e todo o processo se repete.
Exemplos:
Exemplo 1:
>cat file
O gato preto foi caado por um cachorro marrom.
>sed -e 's/preto/branco/g' file
O gato branco foi caado por um cachorro marrom.

Exemplo 2:
>cat file
O gato preto foi caado por um cachorro marrom.
O gato preto no foi caado por um cachorro marrom.
>sed -e '/no/s/preto/branco/g' file
O gato preto foi caado por um cachorro marrom.
O gato branco no caado por um cachorro marrom.

Neste caso, a substituio aplicada somente a linhas que casam com a expresso
regular "/no/". Portanto, ela no aplicada a primeira linha, pois esta no contem a
palavra "no".
Exemplo 3:
>cat file
linha 1 (um)
linha 2 (dois)
linha 3 (trs)

Exemplo 3a:
>sed -e '1,2d' file
linha 3 (trs)

Exemplo 3b:
>sed -e '3d' file
linha 1 (um)
linha 2 (dois)

Exemplo 3c:
>sed -e '1,2s/linha/LINHA/' file
LINHA 1 (um)
LINHA 2 (dois)
linha 3 (trs)

Exemplo 3d:
>sed -e '/^linha.*um/s/linha/LINHA/' -e '/linha/d' file
LINHA 1 (um)
3a : Este foi bem simples: Ns apenas deletamos as linhas de 1
at 2
3b : Isto tambm foi simples: Ns deletamos a linha 3.
3c : Neste exemplo, ns fizemos uma substituio nas
linhas 1 at 2.
3d : Agora este mais interessante e merece algumas
explicaes.

O primeiro comando vai procurar "^linha.*um" e substituir "linha" por "LINHA", ou


seja, somente a primeira linha casa com essa expresso regular. O segundo
comando diz para o sed deletar linhas que contenham a palavra "linha". Assim,
somente as duas ltimas linhas sero deletadas, j que a primeira teve palavra
"linha" substituda por "LINHA".

Exemplo 4:
>cat file
ol
Este texto ser cortado
ol (tambm ser retirado)
ReTiRaDo Tambm!!!
tchau
(1) Este texto no foi apagado
(2) nem este ... ( tchau )
(3) nem este
ol
mas este ser
e este tambm
e a menos que ns encontremos outro tch*u
cada linha at o final do texto ser apagada
>sed -e '/ol/,/tchau/d' file
(1) Este texto no foi apagado
(2) nem este ... ( tchau )
(3) nem este

Isto mostra como o endereamento funciona quando dois endereos so


especificados. O sed encontra o primeiro casamento da expresso "ol" e deleta
todas as linhas lidas na rea de edio at ele encontrar a primeira linha que contm
a expresso "tchau" (est tambm ser apagada). Ele no aplica mais o comando de

deleo para nenhuma linha at encontrar novamente a expresso "ol". Desde que
a expresso "tchau" no ocorre mais em nenhuma linha subseqente, o comando de
deleo aplicado at o final do texto.
Resumindo, simples: Quando ele encontra o primeiro endereo ("ol") ele passa a
executar os comandos at encontrar o segundo endereo ("tchau") ou o fim do
texto. E isso se repete at acabar o texto.
Exemplo 5:
>cat file
http://www.kernel.org/
>sed -e
's@http://www.kernel.org@http://www.metalab.unc.edu@' file
http://www.metalab.unc.edu/

Note que ns usamos um delimitador diferente, @ para o comando de substituio.


O Sed permite diversos delimitadores para o comando de substituio, incluindo @
% , ; : Esses delimitadores alternativos so bons para substituies que incluem
string como nome de arquivos e outras que contm barra normal /, o que torna o
cdigo do sed muito mais legvel.

Mais alguns comandos


Aqui veremos mais alguns comandos sobre o sed, a maioria deles mais complexos.
Dependendo da aplicao em que vocs forem usar o sed, dificilmente usaro esses
recursos, a menos que precisem de um processamento de texto mais refinado e
complexo. Veja que eles so muito usado nos scripts do sed (no confunda com
shell-script).
Backreference no Sed
Uma das coisas legais sobre backreference no sed que voc pode usar no apenas
em procura de textos mas tambm na substituio de textos.
O comando Quit
O comando quit ou "q" muito simples. Ele simplesmente termina o processamento.
Nenhuma outra linha lida para a rea de edio ou impressa na sada padro.
Sub-rotinas
Ns agora introduzimos o conceito de sub-rotinas no sed:
No sed, as chaves { } so usadas para agrupar comandos. Elas so usadas dessa
maneira:
endereo1[,endereo2]{
comandos}

Exemplo: Encontrar uma palavra de uma lista num arquivo


Este exemplo faz um bom uso dos conceitos descritos acima.
Para isto, nos usamos um shell-script, desde que os comandos so muito longos e
precisaramos escrever a longa string X vrias vezes. Note que usamos aspas
duplas, j que a varivel $X precisa ser expandida pelo shell. A sintaxe para rodar
esse script : "script arquivo", onde "script" o nome dado ao script e "arquivo" o
nome do arquivo a procurar uma palavra da lista.
#!/bin/sh
X='word1\|word2\|word3|\word4|\word5'
sed -e "
/$X/!d
/$X/{
s/\($X\).*/\1/

s/.*\($X\)/\1/
q
}" $1
Uma nota importante: tentador pensar que:
s/\($X\).*/\1/
s/.*\($X\)/\1/
redundante e tentar encurta-la para:
s/.*\($X\).*/\1/
Mas isto no funciona. Por que? Suponha que temos a linha:
word1 word2 word3

Ns no temos como saber se $X vai casar com word1, word2 ou word3, ento
quando ns citamos ele (\1), ns no sabemos quais dos termos est sendo citado.
O que est sendo usado para certificar-se que no h problemas na correta
implementao, isto:
"O operador * guloso. Ou seja, quando h ambigidade sobre qual (expresso)*
pode casar, ele tentar casar o mximo possvel."
Ento neste exemplo, s/\($X\).*/\1/ , .* tenta engolir o mximo da linha possvel,
em particular, se a linha contm isso:
"word1 word2 word3"

Ento ns podemos ter certeza que .* casa com " word2 word3" e portanto $X seria
word1. No se preocupem se no entenderam muito bem este tpico. Ele
complicado mesmo e pouco usado. O importante entender como ele funciona, o
uso de regexp e do comando de substituio e deleo. Para maiores informaes
sobre o sed, d uma olhada na sua man page: "man sed"
Espero que vocs tenham gostado desse pequeno tutorial sobre sed. Como disse na
aula anterior sobre o Grip, praticando que se aprende a usar ele.

O fim...
Bom pessoal, acho que nosso curso termina por aqui. Claro que no deu pra cobrir
tudo sobre shell script, se no nosso curso no teria fim. Tentei abordar os topicos
mais importantes, de modo que vcs tenham uma boa noo de como funciona as
coisas no shell e possam dar os proprios passos. Desculpem se esqueci de falar
sobre alguma coisa que vocs estavam esperando. Como disse, estou a disposio
para resolver qualquer dvidas.
Abraos,
Alex Borro

Potrebbero piacerti anche