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

Curso de Shell Script 03/11

  • 1.
    ���������������������������������������������������� ����������������������������������������������������������������������������������������������������������������������������������������������������� 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 ��������������������������������������������������������������������������������
  • 2.
    ��������������������������������������������������� ���������������������������������������������������������������������������������������������������������������������������������������������������� 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 ��������������������������������������������������������������������������������
  • 3.
    ���������������������������������������������������� ����������������������������������������������������������������������������������������������������������������������������������������������������� 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 ��������������������������������������������������������������������������������
  • 4.
    ��������������������������������������������������� ���������������������������������������������������������������������������������������������������������������������������������������������������� 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 ��������������������������������������������������������������������������������
  • 5.
    ���������������������������������������������������� ����������������������������������������������������������������������������������������������������������������������������������������������������� 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 ��������������������������������������������������������������������������������