Revista Do Linux
EDIÇÃO DO MÊS
 CD do Mês

 Capa
 Entrevista
 Corporativo
 Plataforma
 Evento
 Seguranca
 Hakers
 Sistema
 

Papo de botequim parte II

Mestre Julio, também conhecido por Mike Nelson nas praias cariocas, segue seu artigo sobre conceitos e comandos do shell usados em scripts

Suponha que durante a execução de um script você pode, ou não (dependendo do rumo tomado pela execução do programa), criar um arquivo chamado /tmp/seraqueexiste$$. Para não ficar sujeira no seu disco, ao final do script você colocaria uma linha:

rm /tmp/seraqueexiste$$

Caso o arquivo não existisse seria enviada para a tela uma mensagem de erro. Para evitar isso:

rm /tmp/seraqueexiste$$ 2> /dev/null

Nesse exemplo, duas dicas:

    Bizu 1 — O $$ contém o PID, isto é, o número do seu processo. Como o Linux é multiusuário, é bom anexar sempre o $$ ao nome dos seus arquivos para não haver problema de propriedade, isto é, caso você nomeasse o seu arquivo simplesmente como seraqueexiste, a primeira pessoa que o usasse (criando-o então) seria o seu dono e a segunda ganharia um erro ao tentar gravar:

    Bizu 2 — Quem é esse tal de /dev/null? Em Unix existe um arquivo fantasma. Chama-se /dev/null. Tudo que é mandado para este arquivo some. Assemelha-se a um Buraco Negro. Nesse exemplo, como não me interessava guardar a possível mensagem de erro oriunda do rm, redirecionei-a para este arquivo. Para que você teste a Saída de Erro Padrão direto no prompt do seu shell, vou dar mais um exemplo. Faça:

    $ ls naoexiste
    bash: naoexiste no such file or directory
    $ ls naoexiste 2> arquivodeerros
    $
    $ cat arquivodeerros
    bash: naoexiste no such file or directory
    

    Aqui, vimos que quando fizemos um ls em naoexiste, ganhamos uma mensagem de erro. Após redirecionarmos a Saída de Erro Padrão para arquivodeerros e executarmos o mesmo comando, recebemos somente o prompt na tela. Quando listamos o conteúdo do arquivo para o qual foi redirecionada a Saída de Erro Padrão, vimos que a mensagem de erro tinha sido armazenada nele.

    É interessante notar que esses caracteres de redirecionamento são cumulativos, isto é, se no exemplo anterior fizéssemos:

    $ ls naoexiste 2>> arquivodeerros
    

    a mensagem de erro oriunda do ls seria anexada ao final de arquivodeerros.

    Redirecionamento da Entrada Padrão

    Para fazermos o redirecionamento da Entrada Padrão usamos o caractere < (menor que). E para que serve isso, você vai me perguntar. Deixe eu lhe dar um exemplo para você entender.

    Suponha que você queira mandar um mail para o seu chefe. Para o chefe nós caprichamos, não é? Então, em vez de sair redigindo o mail direto no prompt da tela de forma a tornar impossível a correção de uma frase anterior em que vocIe escreveu um "nós vai", você edita um arquivo com o conteúdo da mensagem e, após quinze verificações sem constatar erros, decide enviá-lo e, para tal, faz:

    $ mail chefe < arqmailparaochefe
    

    o seu chefe então receberá o conteúdo do arqmailparaochefe.

    Um outro tipo de redirecionamento muito louco que o shell permite é o chamado here document. Ele é representado por << (menor menor) e serve para indicar ao shell que o escopo de um comando começa na linha seguinte e termina quando encontra uma linha cujo conteúdo seja unicamente o label que segue o sinal <<. Veja esse fragmento de script numa rotina de ftp:

    ftp —ivn hostremoto << fimftp
    		user $Usuário $Senha
    		binary
    		get arquivoremoto
    fimftp
    

    Nesse pedacinho de programa temos um monte de detalhes interessantes:

    1 — As opções que usei para o ftp (-ivn) servem para ele ir listando tudo que está acontecendo (—v de verbose), para não perguntar se você tem certeza de que deseja transmitir cada arquivo (—i de interactive), e finalmente a opção —n serve para dizer ao ftp para ele não solicitar o usuário e sua senha, pois esses serão informados pela instrução específica (user);

    2 — Quando eu usei o << fimftp, estava dizendo o seguinte para o intérprete: "Olhe aqui shell, não se meta em nada a partir daqui até encontrar o label fimftp. Você não entenderia nada, já que são instruções específicas do ftp".

    Se fosse só isso seria simples, mas pelo próprio exemplo dá para ver que existem duas variáveis ($Usuário e $Senha), que o shell vai resolver antes do redirecionamento. Mas a grande vantagem desse tipo de construção é que ela permite que comandos também sejam interpretados dentro do escopo do here document, o que também contraria o que acabei de dizer. Logo a seguir explico como esse negócio funciona. Agora ainda não dá, está faltando ferramenta.

    3 — O comando user é do repertório de instruções do ftp e serve para passar o usuário e a senha que haviam sido lidos em uma rotina anterior a esse fragmento de código e colocados respectivamente nas duas variáveis: $Usuário e $Senha.

    O binary é outra instrução do ftp, que serve para indicar que a transferência de arquivoremoto será feita em modo binário, isto é, o conteúdo do arquivo não será interpretado para saber se está em ASCII, EBCDIC, ...

    O get arquivoremoto diz ao ftp para pegar esse arquivo em hostremoto e trazê-lo para o nosso host local. Se fosse para mandar o arquivo, usaríamos o comando put.

    Está bem, está bem! Eu sei que dei uma viajada e entrei pelos comandos do ftp, fugindo ao nosso assunto que é shell, mas como é sempre bom aprender e é raro as pessoas estarem disponíveis para ensinar...

    Redirecionamento de Comandos

    Os redirecionamentos de que falamos até aqui sempre se referiam a arquivos, isto é, mandavam para arquivo, recebiam de arquivo, simulavam arquivo local, ... O que veremos a partir de agora redireciona a saída de um comando para a entrada de outro. É utilíssimo e quebra os maiores galhos. Seu nome é pipe (que em inglês significa tubo, já que ele encana a saída de um comando para a entrada de outro) e sua representação é | (barra vertical).

    $ ls | wc —l
    21
    

    O comando ls passou a lista de arquivos para o comando wc, que, quando está com a opção —l, conta a quantidade de linhas que recebeu. Dessa forma, podemos afirmar categoricamente que no meu diretório existiam 21 arquivos.

    $ cat /etc/passwd | sort | lp
    

    Esta linha de comandos manda a listagem do arquivo /etc/passwd para a entrada do comando sort. Esse a classifica e manda-a para o lp, que é o gerenciador do spool das impressoras.

    Caracteres de Ambiente

    Quando quer priorizar uma expressão você coloca-a entre parênteses, não é? Pois é, por causa da aritmética é normal pensarmos desse jeito. Mas em shell o que prioriza mesmo são as crases (‘) e não os parênteses. Vou dar exemplos de uso das crases para você entender melhor. Eu quero saber quantos usuários estão "logados" no computador que administro. Posso fazer:

    $ who | wc —l
    8
    

    O comando who passa a lista dos conectados para o comando wc —l, que conta quantas linhas recebeu e lista a resposta na tela. Pois bem, mas em vez de ter um oito solto na tela, o que eu quero é que ele esteja no meio de uma frase. Ora, para mandar frases para a tela eu uso o comando echo, então vamos ver como é que fica:

    $ echo "Existem who | wc —l usuarios conectados"
    Existem who | wc —l usuarios conectados
    

    Ih! Não funcionou! É mesmo, e não foi por causa das aspas que eu coloquei, mas porque eu teria de ter executado o who | wc —l antes do echo. Para resolver esse problema, tenho de priorizar essa 2ª parte do comando com o uso de crases, fazendo assim:

    $ echo "Existem `who | wc —l` usuarios conectados"
    Existem     8 usuarios conectados
    

    Para eliminar esse monte de brancos antes do 8 que o wc —l produziu, basta tirar as aspas. Assim:

    $ echo Existem ‘who | wc —l‘ usuarios conectados
    

    Existem 8 usuarios conectados

    As aspas protegem tudo que está dentro dos seus limites, da interpretação do shell. Como para ele basta um espaço em branco como separador, o monte de espaços será trocado por um único após a retirada das aspas.

    Antes de falar sobre o uso dos parênteses dou uma rapidinha sobre o uso de ponto-e-vírgula. Para agrupar comandos em uma mesma linha teremos de separá-los por (;) :

    $ pwd ; cd /etc; pwd ;cd -;pwd 
    /home/meudir 
    /etc
    /home/meudir
    

    Nesse exemplo, listei o nome do diretório corrente com o comando pwd, mudei para o diretório /etc, novamente listei o nome do diretório e finalmente voltei para o diretório onde estava anteriormente (cd -), listando seu nome. Repare que coloquei (;) de todas as formas possíveis para mostrar que não importa se existe espaço em branco antes ou após esse caractere.

    Vamos ver o caso dos parênteses:

    $ ( pwd ; cd /etc ; pwd )
    /home/meudir
    /etc
    $ pwd
    /home/meudir
    

    Quequeiiisso minha gente? Eu estava no /home/meudir, mudei para o /etc, constatei que estava nesse diretório com o pwd seguinte e quando o agrupamento de comandos terminou, vi que continuava no /home/meudir, como se eu nunca houvesse saído de lá!

    Ih! Será que é o Mister M? Não, o interessante do uso de parênteses é que ele invoca um novo shell para executar os comandos que estão no seu interior. Dessa forma, realmente fomos para o diretório /etc, mas quando todos os comandos dentro dos parênteses foram executados, o novo shell que estava no diretório /etc morreu e voltamos ao shell anterior que estava em /home/meudir. Faça outros testes usando cd, e ls para firmar o conceito.

    Veja só este exemplo a seguir:

    $ mail suporte << FIM
    >Ola suporte, as ‘date"+%hh:mm"‘
    >ocorreu novamente aquele problema
    >que eu havia reportado por 
    >telefone. Conforme seu pedido 
    >ai vai uma listagem dos arquivos 
    >do diretorio:
    >‘ls —l‘
    >Abracos a todos.
    >FIM
    

    Finalmente agora temos conhecimento para mostrar o que havíamos conversado sobre here document. Os comandos entre crases serão priorizados e portanto o shell os executará antes do redirecionamento do here document. Quando o suporte receber o mail, verá que os comandos date e ls foram executados antes do comando mail, recebendo então uma fotografia do ambiente no momento em que a correspondência foi enviada.

    O prompt primário default do shell, como vimos, é o cifrão ($), porém o shell usa o conceito de prompt secundário, ou de continuação de comando, que é enviado para a tela quando há uma quebra de linha e a instrução não terminou. Esse prompt, representado por >, que vemos precedendo a partir da 2ª linha do exemplo.

    Para finalizar e bagunçar tudo, devo dizer que existe uma construção mais moderna que vem sendo utilizada como forma de priorização de execução de comandos, tal qual as crases. Mostro, mas desaconselho, já que nem todos os dialetos shell (como o Bourne Shell) a reconhecem. Essa priorização de comandos é feita assim:

    $(comandos)
    

    Vejamos no exemplo dado para as crases nessa nova ótica:

    $ echo Existem $(who | wc —l) us.cnt
    Existem 8 us.cnt
    

    Olhe, amigo, vá treinando esses exemplos, porque, quando nos encontrarmos novamente, vou lhe explicar uma série de instruções típicas de programação shell. Tchau!

    Ahh! Só mais uma coisinha que eu ia esquecendo de lhe dizer. Em shell, o "jogo da velha" (#) é usado para fazermos um comentário.

    exit # pede a conta ao garcon :(
    

A Revista do Linux é editada pela Conectiva S/A
Todos os Direitos Reservados.

Política de Privacidade
Anuncie na Revista do Linux