You are on page 1of 5

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

�����������������������������������������������������������������������������������������������������������������������������������������������������
Papo de Botequim LINUX USER

Curso de Shell Script

Papo de
botequim III
Um chopinho, um aperitivo e o papo continua. Desta vez vamos aprender
alguns comandos de manipulação de cadeias de caracteres, que serão muito

úteis na hora de incrementar nossa “CDteca”. POR JULIO CEZAR NEVES

G arçon! traga dois chopes por


favor que hoje eu vou ter que
falar muito. Primeiro quero
mostrar uns programinhas simples
de usar e muito úteis, como o cut, que
Então, recapitulando, o layout
do arquivo é o seguinte: nome do
álbum^intérprete1~nome da música1:...:
intérpreten~nome da músican, isto é, o
nome do álbum será separado por um
Artista2
Artista4
Artista6
Artista8

é usado para cortar um determinado circunflexo (^) do resto do registro, que Para entender melhor isso, vamos anali-
pedaço de um arquivo. A sintaxe e é formado por diversos grupos compos- sar a primeira linha de musicas:
alguns exemplos de uso podem ser vis- tos pelo intérprete de cada música do
tos no Quadro 1: CD e a respectiva música interpretada. $ head -1 musicas
Como dá para ver, existem quatro Estes grupos são separados entre si por album 1^Artista1~Musica1: U
sintaxes distintas: na primeira (-c 1-5) dois-pontos (:) e o intérprete será sepa- Artista2~Musica2
especifiquei uma faixa, na segunda rado do nome da música por um til (~).
(-c -6) especifiquei todo o texto até Então, para pegar os dados referentes Então observe o que foi feito:
uma posição, na terceira (-c 4-) tudo de a todas as segundas músicas do arquivo
uma determinada posição em diante e musicas, devemos digitar: album 1^Artista1~Musica1: U
na quarta (-c 1,3,5,7,9), só as posições Artista2~Musica2
determinadas. A última possibilidade $ cut -f2 -d: musicas
(-c -3,5,8-) foi só para mostrar que pode- Artista2~Musica2 Desta forma, no primeiro cut o primeiro
mos misturar tudo. Artista4~Musica4 campo do delimitador (-d) dois-pon-
Mas não pense que acabou por aí! Artista6~Musica5 tos (:) é album 1^Artista1~Musica1 e o
Como você deve ter percebido, esta Artista8~Musica8@10_L: segundo, que é o que nos interessa, é
forma de cut é muito útil para lidar com Artista2~Musica2. Vamos então ver o
arquivos com campos de tamanho fi xo, Ou seja, cortamos o segundo campo que aconteceu no segundo cut:
mas atualmente o que mais existe são z(-f de field, campo em inglês) delimi-
arquivos com campos de tamanho vari- tado (-d) por dois-pontos (:). Mas, se Artista2~Musica2
ável, onde cada campo termina com um quisermos somente os intérpretes, deve-
delimitador. Vamos dar uma olhada no mos digitar: Agora, primeiro campo do delimitador
arquivo musicas que começamos a pre- (-d) til (~), que é o que nos interessa,
parar na última vez que viemos aqui no $ cut -f2 -d: musicas | cut U é Artista2 e o segundo é Musica2. Se
botequim. Veja o Quadro 2. -f1 -d~ o raciocínio que fizemos para a pri-

�����������������������������
www.linuxmagazine.com.br Outubro 2004 85
��������������������������������������������������������������������������������
���������������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������������������������
LINUX USER Papo de Botequim

Quadro 1 – O comando cut meira linha for aplicado ao restante do


arquivo, chegaremos à resposta ante-
posta a um exercício prático, valendo
nota, que passei ele me entregou um
riormente dada. Outro comando muito script com todos os comandos sepa-
A sintaxe do cut é: cut -c PosIni-PosFim
interessante é o tr que serve para subs- rados por ponto-e-vírgula (lembre-se
[arquivo], onde PosIni é a posição inicial, e
tituir, comprimir ou remover caracteres. que o ponto-e-vírgula serve para sepa-
PosFim a posição final. Veja os exemplos:
Sua sintaxe segue o seguinte padrão: rar diversos comandos em uma mesma
linha). Vou dar um exemplo simplifi-
$ cat numeros
tr [opções] cadeia1 [cadeia2] cado, e idiota, de um script assim:
1234567890
0987654321
O comando copia o texto da entrada $ cat confuso
1234554321
padrão (stdin), troca as ocorrência dos echo leia Programação Shell U
9876556789
caracteres de cadeia1 pelo seu corres- Linux do Julio Cezar Neves U
pondente na cadeia2 ou troca múltiplas > livro;cat livro;pwd;ls;rm U
$ cut -c1-5 numeros
ocorrências dos caracteres de cadeia1 -f livro2>/dev/null;cd ~
12345
por somente um caracter, ou ainda
09876
caracteres da cadeia1. As principais Eu executei o programa e ele funcionou:
12345
opções do comando são mostradas na
98765
Tabela 1. $ confuso
Primeiro veja um exemplo bem bobo: leia Programação Shell Linux U
$ cut -c-6 numeros
do Julio Cezar Neves
123456
$ echo bobo | tr o a /home/jneves/LM
098765
baba confuso livro musexc musicas
123455
musinc muslist numeros
987655
Isto é, troquei todas as ocorrências da
letra o pela letra a. Suponha que em Mas nota de prova é coisa séria (e nota
$ cut -c4- numeros
determinado ponto do meu script eu de dólar é mais ainda) então, para
4567890
peça ao operador para digitar s ou n entender o que o aluno havia feito, o
7654321
(sim ou não), e guardo sua resposta chamei e em sua frente digitei:
4554321
na variável $Resp. Ora, o conteúdo de
6556789
$Resp pode conter letras maiúsculas $ tr ”;” ”\n” < confuso
ou minúsculas, e desta forma eu teria echo leia Programação Shell U
$ cut -c1,3,5,7,9 numeros
que fazer diversos testes para saber se Linux do Julio Cezar Neves
13579
a resposta dada foi S, s, N ou n. Então o pwd
08642
melhor é fazer: cd ~
13542
ls -l
97568
$ Resp=$(echo $Resp | tr SN sn) rm -f lixo 2>/dev/null

$ cut -c -3,5,8- numeros


e após este comando eu teria certeza O cara ficou muito desapontado, porque
1235890
de que o conteúdo de $Resp seria um em dois ou três segundos eu desfi z a
0986321
s ou um n. Se o meu arquivo ArqEnt gozação que ele perdeu horas para fazer.
1235321
está todo em letras maiúsculas e desejo Mas preste atenção! Se eu estivesse em
9875789
passá-las para minúsculas eu faço: uma máquina Unix, eu teria digitado:

$ tr A-Z a-z < ArqEnt > / tmp/$$ $ tr ”;” ”\012” < confuso
Quadro 2 – O arquivo $ mv -f /tmp/$$ ArqEnt
musicas Agora veja a diferença entre o resultado
Note que neste caso usei a notação A- de um comando date executado hoje e
Z para não escrever ABCD…YZ. Outro outro executado há duas semanas:
$ cat musicas
tipo de notação que pode ser usada são
album 1^Artista1~Musica1:U
as escape sequences (como eu traduzi- Sun Sep 19 14:59:54 2004
Artista2~Musica2
ria? Seqüências de escape? Meio sem Sun Sep 5 10:12:33 2004
album 2^Artista3~Musica3:U
sentido, né? Mas vá lá…) que também
Artista4~Musica4
são reconhecidas por outros comandos Notou o espaço extra após o “Sep” na
album 3^Artista5~Musica5:U
e também na linguagem C, e cujo signi- segunda linha? Para pegar a hora eu
Artista6~Musica5
ficado você verá na Tabela 2: deveria digitar:
album 4^Artista7~Musica7:U
Deixa eu te contar um “causo”: um
Artista8~Musica8
aluno que estava danado comigo resol- $ date | cut -f 4 -d ’ ’
veu complicar minha vida e como res- 14:59:54

�����������������������������
86 Outubro 2004 www.linuxmagazine.com.br
��������������������������������������������������������������������������������
����������������������������������������������������
�����������������������������������������������������������������������������������������������������������������������������������������������������
Papo de Botequim LINUX USER

Mas há duas semanas ocorreria o Bem a opção -d do tr remove do arquivo E veja também o date:
seguinte: todas as ocorrências do caractere espe-
cificado. Desta forma eu removi os $ date
$ date | cut -f 4 -d ’ ’ caracteres indesejados, salvei o texto Mon Sep 20 10:47:19 BRT 2004
5 em um arquivo temporário e posterior-
mente renomeei-o para o nome original. Repare que o mês e o dia estão no
Isto porque existem 2 caracteres em Uma observação: em um sistema Unix mesmo formato em ambos os coman-
branco antes do 5 (dia). Então o ideal eu deveria digitar: dos. Ora, se em algum registro do who
seria transformar os espaços em branco eu não encontrar a data de hoje, é sinal
consecutivos em somente um espaço $ tr -d ’\015’ < ArqDoDOS.U que o usuário está “logado” há mais
para poder tratar os dois resultados do txt > /tmp/$$ de um dia, já que ele não pode ter se
comando date da mesma forma, e isso “logado” amanhã… Então vamos guar-
se faz assim: Uma dica: o problema com os termina- dar o pedaço que importa da data de
dores de linha (CR/LF) só aconteceu hoje para depois procurá-la na saída do
$ date | tr -s ” ” porque a transferência do arquivo foi comando who:
Sun Sep 5 10:12:33 2004 feita no modo binário (ou image), Se
antes da transmissão do arquivo tivesse $ Data=$(date | cut -f 2-3 U
E agora eu posso cortar: sido estipulada a opção ascii do ftp, isto -d’ ’)
não teria ocorrido.
$ date | tr -s ” ” | cut -f 4 U – Olha, depois desta dica tô começando a Eu usei a construção $(...), para prio-
-d ” ” gostar deste tal de shell, mas ainda tem rizar a execução dos comandos antes
10:12:33 muita coisa que não consigo fazer. de atribuir a sua saída à variável Data.
– Pois é, ainda não te falei quase nada Vamos ver se funcionou:
Olha só como o Shell está quebrando o sobre programação em shell, ainda
galho. Veja o conteúdo de um arquivo tem muita coisa para aprender, mas $ echo $Data
baixado de uma máquina Windows: com o que aprendeu, já dá para resol- Sep 20
ver muitos problemas, desde que você
$ cat -ve ArqDoDOS.txt adquira o “modo shell de pensar”. Você Beleza! Agora, o que temos que fazer é
Este arquivo^M$ seria capaz de fazer um script que diga procurar no comando who os registros
foi gerado pelo^M$ quais pessoas estão “logadas” há mais que não possuem esta data.
DOS/Win e foi^M$ de um dia no seu servidor?
baixado por um^M$ – Claro que não! Para isso seria necessá- – Ah! Eu acho que estou entendendo!
ftp mal feito.^M$ rio eu conhecer os comandos condicio- Você falou em procurar e me ocorreu o
nais que você ainda não me explicou comando grep, estou certo?
Dica: a opção -v do cat mostra os carac- como funcionam. - Deixa eu tentar – Certíssimo! Só que eu tenho que usar o
teres de controle invisíveis, com a nota- mudar um pouco a sua lógica e trazê- grep com aquela opção que ele só lista
ção ^L, onde ^ é a tecla Control e L é la para o “modo shell de pensar”, mas os registros nos quais ele não encon-
a respectiva letra. A opção -e mostra o antes é melhor tomarmos um chope. trou a cadeia. Você se lembra que
fi nal da linha como um cifrão ($). Agora que já molhei a palavra, vamos opção é essa?
Isto ocorre porque no DOS o fi m dos resolver o problema que te propus. Veja – Claro, a -v…
registros é indicado por um Carriage como funciona o comando who: – Isso! Tá ficando bom! Vamos ver:
Return (\r – Retorno de Carro, CR) e
um Line Feed (\f – Avanço de Linha, ou $ who $ who | grep -v ”$Data”
LF). No Linux porém o fi nal do regis- jneves pts/ U jneves pts/ U
tro é indicado somente pelo Line Feed. 1 Sep 18 13:40 1 Sep 18 13:40
Vamos limpar este arquivo: rtorres pts/ U
0 Sep 20 07:01 Se eu quisesse um pouco mais de perfu-
$ tr -d ’\r’ < ArqDoDOS.txt > /tmp/$$ rlegaria pts/ U maria eu faria assim:
$ mv -f /tmp/$$ ArqDoDOS.txt 1 Sep 20 08:19
lcarlos pts/ U $ who | grep -v ”$Data” |U
Agora vamos ver o que aconteceu: 3 Sep 20 10:01 cut -f1 -d ’ ’
jneves
$ cat -ve ArqDoDOS.txt Tabela 1 – O comando tr
Este arquivo$ Opção Significado Viu? Não foi necessário usar comando
foi gerado pelo$ condicional, até porque o nosso
-s Comprime n ocorrências de
DOS/Rwin e foi$ cadeia1 em apenas uma
comando condicional, o famoso if, não
baixado por um$ testa condição, mas sim instruções.
-d Remove os caracteres de cadeia1
ftp mal feito.$ Mas antes veja isso:

�����������������������������
www.linuxmagazine.com.br Outubro 2004 87
��������������������������������������������������������������������������������
���������������������������������������������������
����������������������������������������������������������������������������������������������������������������������������������������������������
LINUX USER Papo de Botequim

$ ls musicas Tabela 2 /dev/null


musicas Seqüência Significado Octal then
$ echo $? \t Tabulação \011 echo Usuario \’$1\’ já U
0 \n Nova linha \012 existe
$ ls ArqInexistente <ENTER> else
ls: ArqInexistente: No such U \v Tabulação \013 if useradd $1
file or directory Vertical then
$ echo $? \f Nova \014 echo Usuário \’$1\’ U
1 Página incluído em /etc/passwd
$ who | grep jneves \r Início da \015 else
jneves pts/1 Sep 18 U linha <^M> echo ”Problemas no U
\\ Uma barra \0134
13:40 (10.2.4.144) cadastramento. Você é root?”
invertida
$ echo $? fi
0 fi
$ who | grep juliana já existe
$ echo $? else Vamos testá-lo como um usuário normal :
1 if useradd $1
then $ incusu ZeNinguem
O que é que esse $? faz aí? Algo come- echo Usuário \’$1\’ U ./incusu[6]: useradd: not found
çado por cifrão ($) parece ser uma vari- incluído em /etc/passwd Problemas no cadastramento. U
ável, certo? Sim é uma variável que else Você é root?
contém o código de retorno da última echo ”Problemas no U
instrução executada. Posso te garan- cadastramento. Você é root?” Aquela mensagem de erro não deveria
tir que se esta instrução foi bem suce- fi aparecer! Para evitar isso, devemos
dida, $? terá o valor zero, caso contrário fi redirecionar a saída de erro (stderr) do
seu valor será diferente de zero. O que comando useradd para /dev/null. A ver-
nosso comando condicional (if) faz é Repare que o if está testando direto o são fi nal fica assim:
testar esta variável. Então vamos ver a comando grep e esta é a sua fi nalidade.
sua sintaxe: Caso o if seja bem sucedido, ou seja, o $ cat incusu
usuário (cujo nome está em $1) foi #!/bin/bash
if cmd encontrado em /etc/passwd, os coman- # Versão 3
then dos do bloco do then serão executados if grep ^$1 /etc/passwd > U
cmd1 (neste exemplo, apenas o echo). Caso /dev/null
cmd2 contrário, as instruções do bloco do else then
cmdn serão executadas, quando um novo if echo Usuario \’$1\’ já U
else testa se o comando useradd foi execu- existe
cmd3 tado a contento, criando o registro do else
cmd4 usuário em /etc/passwd, ou exibindo if useradd $1 2> /dev/null
cmdm uma mensagem de erro, caso contrário. then
fi Executar o programa e passe como echo Usuário \’$1\’ U
parâmetro um usuário já cadastrado: incluído em /etc/passwd
Ou seja, caso comando cmd tenha sido else
executado com sucesso, os comandos $ incusu jneves echo ”Problemas no U
do bloco do then (cmd1, cmd2 e cmdn) jneves:x:54002:1001:Julio Neves: U cadastramento. Você é root?”
serão executados, caso contrário, os /home/jneves:/bin/ U fi
comandos do bloco opcional do else bash fi
(cmd3, cmd4 e cmdm) serão executados. Usuario ’jneves’ ja existe
O bloco do if é terminando com um fi. Depois disso, vejamos o comportamento
Vamos ver na prática como isso fun- No exemplo dado, surgiu uma linha do programa, se executado pelo root:
ciona, usando um script que inclui usu- indesejada, ela é a saída do comando
ários no arquivo /etc/passwd: grep. Para evitar que isso aconteça, $ incusu botelho
devemos desviar a saída para /dev/null. Usuário ’botelho’ incluido em U
$ cat incusu O programa fica assim: /etc/passwd
#!/bin/bash
# Versão 1 $ cat incusu E novamente:
if grep ^$1 /etc/passwd #!/bin/bash
then # Versão 2 $ incusu botelho
echo Usuario \’$1\’U if grep ^$1 /etc/passwd > U Usuário ’botelho’ já existe

�����������������������������
88 Outubro 2004 www.linuxmagazine.com.br
��������������������������������������������������������������������������������
����������������������������������������������������
�����������������������������������������������������������������������������������������������������������������������������������������������������
Papo de Botequim LINUX USER

Lembra que eu falei que ao longo dos no início da cadeia e cifrão ($) no fi m, Como você viu, o programa melho-
nossos papos e chopes os nossos pro- servem para testar se o parâmetro (o rou um pouquinho, mas ainda não está
gramas iriam se aprimorando? Então álbum e seus dados) é exatamente igual pronto. À medida que eu te ensinar a
vejamos agora como podemos melhorar a algum registro já existente. Vamos programar em shell, nossa CDteca vai
o nosso programa para incluir músicas executar nosso programa novamente, ficar cada vez melhor.
na "CDTeca": mas desta vez passamos como parâme- – Entendi tudo que você me explicou,
tro um álbum já cadastrado, pra ver o mas ainda não sei como fazer um if
$ cat musinc que acontece: para testar condições, ou seja o uso
#!/bin/bash normal do comando.
# Cadastra CDs (versao 3) $ musinc ”album 4^Artista7~ U – Para isso existe o comando test, que
# Musica7:Artista8~Musica8” testa condições. O comando if testa
if grep ”^$1$” musicas > U Este álbum já está cadastrado o comando test. Como já falei muito,
/dev/null preciso de uns chopes para molhar a
then E agora um não cadastrado: palavra. Vamos parar por aqui e na
echo Este álbum já está U próxima vez te explico direitinho o
cadastrado $ musinc ”album 5^Artista9~ U uso do test e de diversas outras sinta-
else Musica9:Artista10~Musica10” xes do if.
echo $1 >> musicas $ cat musicas – Falou! Acho bom mesmo porque eu
sort musicas -o musicas album 1^Artista1~Musica1: U também já tô ficando zonzo e assim
fi Artista2~Musica2 tenho tempo para praticar esse monte
album 2^Artista3~Musica3: U de coisas que você me falou hoje.
Como você viu, é uma pequena evolu- Artista4~Musica4 Para fi xar o que você aprendeu, tente
ção em relação à versão anterior. Antes album 3^Artista5~Musica5: U fazer um scriptizinho para informar se
de incluir um registro (que na versão Artista6~Musica5 um determinado usuário, cujo nome
anterior poderia ser duplicado), testa- album 4^Artista7~Musica7: U será passado como parâmetro, está
mos se o registro começa (^) e termina Artista8~Musica8 “logado” no sistema ou não.
($) de forma idêntica ao parâmetro album 5^Artista9~Musica9: U – Aê Chico! traz mais dois chopes pra
álbum passado ($1). O circunflexo (^) Artista10~Musica10 mim por favor… ■

�����������������������������
www.linuxmagazine.com.br Outubro 2004 89
��������������������������������������������������������������������������������

You might also like