1. Operações com Arquivo e Filtragem

A maior parte do trabalho em linha de comando é feito em arquivos. Nesta seção nós iremos mostrar a você como observar e filtrar o conteúdo de um arquivo, como extrair a informação necessária de arquvo utilizado um único comando, e como ordenar facilmente o conteúdo de um arquivo.

1.1. cat, tail, head, tee: Comandos para Impressão de Arquivos

Estes comandos possuem quase a mesma sintaxe: nome_do_comando [opção(ões)] [arquivo(s)], e podem ser utilizados em um pipe. Todos eles são utilizados para imprimir parte de um arquivo de acordo com um certo critério.

O cat concatena arquivos e imprime o resultado para a saída padrão, a qual é normalmente a tela do seu computador. Este é um dos comandos mais utilizados. Por exemplo, você pode usar:

# cat /var/log/mail/info

para imprimir o conteúdo do arquivo de log de um daemon de correio na saída padrão[23]. O comando cat possui uma opção muito útil(-n) que permite imprimir também o número de cada linha do arquivo.

Alguns arquivos, como os logs de daemons (se estiverem em execução) normalmente são bem grandes em tamanho[24] e imprimí-los na tela completamente não é uma idéia muito útil. De maneira geral, você somente precisa ver algumas linhas do arquivo. Você pode usar o comando tail para fazer isso. O seguinte comando irá mostrar, por padrão, as últimas 10 linhas do arquivo /var/log/mail/info:

# tail /var/log/mail/info

Arquivos como os de log normalmente variam dinamicamente porque o daemon adiciona constantemente ações e eventos ao arquivo. Para observar interativamente estas mudaas, você pode obter vantagem da opção -f:

# tail -f /var/log/mail/info

Neste caso todas as mudanças no arquivo /var/log/mail/info serão imediantamente apresentadas na tela. Utilizar o comando tail com a opção -f é muito útil quando você quer saber como o seu sistema funciona. Por exemplo, observando o arquivo /var/log/messages, você pode acompanhar mensagens do sistema e vários daemons.

Se você utilizar o tail com mais de um arquivo ele irá exibir o nome do arquivo em uma linha antes de apresentar o seu conteúdo. Isto também funciona com a opção -f e é uma valiosa funcionalidade para ver como diferentes partes do sistema interagem.

Você pode utilizar a opção -n para exibir as últimas n linhas de um arquivo. Por exemplo, para exibir as últimas duas linhas, vocẽ deveria executar:

# tail -n2 /var/log/mail/info

Assim como em outroscomandos, você poderia utilizar opções diferentes ao mesmo tempo. Por exemplo, utilizando junto as opções -n2 e -f, você começa com as duas últimas linhas do arquivo e continua a ser informado assim que novas linhas foem exibidas ao arquivo de log.

O comando head é similar ao tail, mas ele exibe as primeiras linhas de um arquivo. O comando a seguir irá exibir, por padrão, as dez primeiras linhas do arquivo /var/log/mail/info:

# head /var/log/mail/info

Assim como o tail você pode utilizar a opção -n para especificar o número de linhas a ser exibido. Por exemplo, para imprimir as duas primeiras, digite:

# head -n2 /var/log/mail/info

Você também pode utilizar estes comandos juntos. Por exemplo, se você quiser exibir somente as linhas 9 e 10 de um arquivo, você pode usar um comando onde o head irá selecionar as 10 primeiras linhas e passá-las através de um pipe para o comando tail.

# head /var/log/mail/info | tail -n2

A últma parte irá então selecionar as duas últimas linhas e irá exibí-las na tela. Da mesma maneira você pode selecionar a vigésima linha a partir do fim do arquivo:

# tail -n20 /var/log/mail/info |head -n1

Neste exemplo nós dizemos ao tail para selecionar as últimas vinte linhas do arquivo e passá-las através do pipe para o comando head. Então o head exibe na tela a primeira linha das informações obtidas.

Vamos supor que queremos exibir o resultado do último exemplo na tela e salvá-lo no arquivo resultados.txt. Para isso, vamos utilizar o comando tee, que possui a seguinte sintaxe:

tee [opções] [arquivo]

Agoranós podemos alterar o comando anterior desta maneira:

# tail -n20 /var/log/mail/info |head -n1|tee resultados.txt

Vamos pegar um outro exemplo. Nós queremos selecionar as últimas 20 linhas, salvá-las no arquivo resultados.txt, mas imprimir na tela somente a primeira linha das 20 selecionadas. Então deveríamos executar:

# tail -n20 /var/log/mail/info |tee resultados.txt |head -n1

O comando tee possui uma opção poderosa (-a) que lhe possibilita adicionar dados a um arquivo existente.

Na próxima seção nós iremos ver como utilizar o comando grep como um filtro para separar as mensagens do Postfix de mensagens geradas por outros serviços.

1.2. grep: Localizando Strings em um Arquivo

Nem o nome e nem o acrônimo (“General Regular Expression Parser” - “Analisador de Expressões Regulares”) são muito intuitivos, mas o que ele faz e o seu uso são muito simples: o grep procura em um ou mais arquivos por um padrão padrão passado como um argumento. A sua sintaxe é

grep [opções] <padrão> [um ou mais arquivo(s)]

Se for indicado mais de um arquivo, o nome de cada arquvo é mecionado no começo de cada linha do resultado. Você pode usar a opção -h para evitar que o nome seja exibido ou então a opção -l para retornar apenas o nome dos arquivos. O padrão é uma expressão regular, embora na maior parte das vezes ela seja uma única palavra. As opções mais comuns são:

  • -i: ignora a diferença entre letras maiúsculas e minúsculas durante a busca;

  • -v: inverte a pesquisa. Exibe linhas que não combinam com o padrão;

  • -n: exibe o número da linha para cada linha encontrada;

  • -w: indica para o grep que o padrão deve ser uma palavra inteira.

Então vamos voltar a analizaro arquivo de log do servidor de e-mails. Nós queremos encontrar todas as linhas no arquivo /var/log/mail/info que contém o padrão postfix. Então nós digitamos este comando:

# grep postfix /var/log/mail/info

Se quisermos encontrar todas as linhas que NÃO possuem o padrão postfix, nós utilizamos então a opção -v:

# grep -v postfix /var/log/mail/info

O comando grep pode ser utilizado com o pipe.

Vamos supor que queremos encontrar todas as mensagens sobre os e-mails enviados com sucesso. neste caso temos que filtrar todas as linhas que foram adicionadas ao arquivo de log pelo servidor de e-mails (ou seja, que contenham o padrão postfix) e elas devem conter ma mensagem sobre o sucesso do envio (status=sent)[25]:

# grep postfix /var/log/mail/info |grep status=sent

Neste caso o comando grep é utilizado duas vezes. É permitido, mas não muito elegante. O mesmo resultado pode ser alcançado com o utilitário fgrep. O fgrep é na verdade um método simples de chamar o grep -F. Primeiro nós precisamos criar um arquivo contendo cada padrão em um linha. Este arquivo pode ser criado da seguinte maneira (nós usaremos padrao.txt como o nome do arquivo:

# echo -e 'status=sent\npostfix' >./padroes.txt

Verifique o resultado com o comando cat. O \n é um padrão especial que significa “nova linha”.

assim vamos executar o próximo comando com o padroes.txt e o aplicativo fgrep em vez de chamar duas vezes o grep:

# fgrep -f ./padrao.txt /var/log/mail/info

O arquivo ./padrao.txt pode conter quantos padrões você quiser. Por exemplo, para selecionar as mensagens sobre e-mails enviados com sucesso para usuario2@mandriva.com, seria o suficiente adicionar este padrão no nosso arquivo ./padroes.txt, executando este comando:

# echo 'usuario2@mandriva.com' >>./padroes.txt

É claro que você pode combinar o grep com o tail e head. Se nós queremos encontrar mesagens sobre o penúltimo e-mail enviado para usuario2@mandriva.com, nós digitaríamos:

# fgrep -f ./padroes.txt /var/log/mail/info | tail -n2 | head -n1

aqui nós aplicamos o filrto descrito acima e direcionamos o resultado em um pipe para os comandos tail e head. Eles selecionam a penúltima linha da lista.

1.3. Expressões Regularem e Filtros com o egrep

Com o grep nós estamos presos com padrões e dados estáticos. Como nós poderíamos encontrar cada e-mails enviados para qualquer funcionário da “ABC Company”? Listar todos os e-mails deles não seria uma tarefa fácil já que poderíamos acabar esquecendo um ou ter que analisar o arquivo de log manualmente.

Assim como o fgrep, o grep também possui um atalho para o comando grep -E: egrep. Ele recebe expressões regulares em vez de padrões, oferecendo uma interface mais poderosa para a busca de texto.

Além do que nós mencionamos na Seção 3, “Casamento de Padrões no Shell” sobre os caracteres curingas, aqui estão mais algumas expressões regulares:

  • [:alnum:], [:alpha:] e [:digit:] podem ser utilizados para evitar que você tenha que definir manualmente classes de caractere que representem, respectivamente, todas as letras mais todos os números, todas as letras (em caixa baixa ou alta), e todos os números. Estas expressões também oferecem um bônus adicional: eles incluem caracteres de internacionalização e respeitam a localização do sistema.

  • [:print:] representa todos os caracteres que podem ser exibidos na tela.

  • [:lower:] e [:upper:] representam lodas as letras de caixa baixa e alta.

Há mais classes disponíveis e você pode ver todas elas na página do manual do egrep(1). As citadas acima são as mais utilizadas.

Uma expressão regular pode ser acomanhada de diversos operadores de repetição:

?

O item que preceder este operador é opcional, isto é, pode haver zero ou uma ocorrência do caractere, mas não mais do que uma.

*

O item que estiver precedendo este operador pode aparecer zero ou mais vezes.

+

Indica que o item pode aparecer uma ou mais vezes.

{n}

Informa que o item deve aparecer exatamente n vezes.

{n,}

Combina n ou mais vezes o item preecdente a este operador.

{n,m}

Busca por no mínimo n ocorrências do item precedente, mas não mais do que m vezes.

Se você colocar uma expressão regular dentro de parênteses, você pode reaproveitá-la em outro local. Vamos dizer que você especificou a expressão [:alpha:]+, que poderia representar uma palavra. Então, se você quiser detectar palavras que aparecem duas vezes você poderia colocar esta expressão dentro ed parênteses e fazer uma referência a ela com \1 que indica o primeiro grupo do padrão. Você pode ter até 9 destas “memórias”.

$ echo -e "abc def\nabc abc def\nabc1 abc1\nabcdef\nabcdabcd\nabcdef abcef" > arquivoteste
$ egrep "([[:alpha:]]+) \1" arquivoteste
abc abc def
$
[Nota]Nota

Os caracteres [ e ] fazem parte do nome do grupo, então nós precisamos incluí-lo para usar esta classe de caracteres. O primeiro [ informa que nós estamos utilizado um grupo de caracteres, o segundo faz parte do nome do grupo, e então há os caracteres ] que fecham cada um dos grupos abertos.

A linha apresentada no resultado é a única que atende o padrão de dois grupos de letras separados por um espaço. Nenhuma outra linha do arquivo combinou com a expressão regular.

Você também pode utilizar o caractere | para informar que o padrão deve casar com a expresão à esquerda ou à direita do |. Utilizando o mesmo arquivo que criamos no exemplo anterior (arquivoteste), você poed tentar procurar por expressões que contenham palavras duplicadas ou palavras duplicadas que contenham números:

$ egrep "([[:alpha:]]+) \1|([[:alpha:][:digit:]]+) \2" arquivoteste
abc abc def
abc1 abc1
$

Note que para o segundo grupo entre parênteses nós utilizamos \2, caso contrário ele não combinaria com o padrão que esperávamos. Uma expressão mais eficiente, para este caso em particular, seria:

$ egrep "([[:alnum:]]+) \1" testfile
abc abc def
abc1 abc1
$

Finalmente, para combinar certos caracteres será necessário “escapá-los”, colocando uma contrabarra antes deles. Estes caracteres são: ?, +, {, |, (, ) e \. Quando quiser buscar por estes caracteres, você terá que escrever: \?, \+, \{, \|, \(, \), e \\.

Este simples truque pode evitar que você tenha palavras repetidas em seu texto.

Expressões regulares em todas as ferramentas devem seguir estas regras, ou ao menos regras similares a estas. Dedicar um pouco de tempo para entendê-las irá ajudar muito quando você trabalhar com ferramentas como o sed. O sed permite que você anipule texto, alterando-o com o uso de expressões regulares e outras coisas.

1.4. wc: Contando Elementos em Arquivos

O comando wc (Word Count - Contagem de Palavras) é utilizado para contar o número de linhas, palavras e caracteres nos arquivos. Ele também pode informar o tamanho da maior linha. Sua sintaxe é:

wc [opções] [arquvo(s)]

As seguintes opções são bastante utilizadas:

  • -l: imprime o número das linhas;

  • -w: imprime o número de palavras;

  • -m: imprime o número total de caracteres;

  • -c: imprime o número de bytes;

  • -L: imprime o tamanho da maior linha no texto.

O comando wc, por padrão, imprime o número de linhas, palavras e caracteres. Aqui estão alguns exemplos de uso:

Se quisermos encontrar o número de usuários em nossos sistema, por exemplo, nós poderíamos digitar:

$ wc -l /etc/passwd 

Se quisermos saber o número de processadores em nosso sistema, nós podemos executar:

$ grep "model name" /proc/cpuinfo |wc -l

Na seção anterior nós obtivemos uma lista de mensagens sobre e-mails enviados com sucesso para os endereços listados no arquivo ./padroes.txt. Se quisermos saber quantas mensagens ele contém, podemos redirecionar o resultado do filtro para o comando wc através de um pipe:

# fgrep -f ./padroes.txt /var/log/mail/info | wc -l

1.5. sort: Ordenando o Conteúdo de um Arquivo

Aqui está a sintaxe deste poderoso utilitário para ordenação[26]:

sort [opções] [arquivo(s)]

Vamos considerar a ordenação em parte do arquivo /etc/passwd. Como você pode ver, o arquivo não é ordenado:

$ cat /etc/passwd

Se nós quisermos ordená-lo pelo campo de login, nós podemos digitar:

$ sort /etc/passwd

O comando sort ordena as informações em ordem crescente iniciando pelo primeiro campo por padrão (no nosso caso, o campo login). Para ordenar em ordem decrescente, use a opção -r:

$ sort -r /etc/passwd

Todo usuário possui um UID informado no arquivo /etc/passwd. O seguinte comando ordena um arquivo em ordem crescente utilizando o campo UID:

$ sort /etc/passwd -t":" -k3 -n

aqui nós utilizamos as seguintes opções do sort:

  • -t":": tells sort that the field separator is the ":" symbol;

  • -k3: means that sorting must be done on the third column;

  • -n: says that the sort is to occur on numerical data, not alphabetical.

Também pode ser ordenado de maneira decrescente:

$ sort /etc/passwd -t":" -k3 -n -r

O sort também possui outras duas opções importantes:

  • -u: elimina entradas duplicadas durante a ordenação;

  • -f: trata de maneira igual os caracteres em caixa baixa ou alta.

Finalmente, se quisermos encontrar o usuário com o maior UID, nós podemos utilizar este comando:

$ sort /etc/passwd -t":" -k3 -n |tail -n1

onde ordenamos o arquivo /etc/passwd em ordem crescente de acordo com a coluna UID, e redirecionamos o resultado através de um pipe para o comando tail, que imprime o último ítem da lista.



[23] Alguns exemplos nesta seção são baseadas em trabalho de verdade e arquivos de log de servidores (serviços, daemons, etc.). Certifique-se de que o syslogd (que permite o registro das atividades), e o daemon correspondente (no nosso caso, o Postfix) esteja sendo executado, e que você esteja como root.É claro, você pode sempre aplicar os nossos exemplos em outros arquivos.

[24] Por exemplo, o arquivo /var/log/mail/info contém informação sobre todos os e-mails enviados, mensagens sobre e-mails recebidos por usuários com o protocolo POP, etc.

[25] Embora seja possível filtrar apenas pelo padrão do do status, iremos fazer a busca desta maneira para lhe apresentar um novo comando com este exemplo.

[26] Nós vamos discutir o brevemente o sort aqui. Livros inteiros podem ser escritos sobre as suas funcionalidades.