103.4 Lição 1
Certificação: |
LPIC-1 |
---|---|
Versão: |
5.0 |
Tópico: |
103 Comandos GNU e Unix |
Objetivo: |
103.4 Usando fluxos, pipes e redirecionamentos |
Lição: |
1 de 2 |
Introdução
Todos os programas de computador seguem o mesmo princípio geral: os dados recebidos de alguma fonte são transformados para gerar um resultado inteligível. No contexto do shell do Linux, a fonte de dados pode ser um arquivo local, um arquivo remoto, um dispositivo (como um teclado), etc. A saída do programa geralmente é exibida em uma tela, mas também é comum armazenar os dados de saída em um sistema de arquivos local, enviar para um dispositivo remoto, reproduzi-lo em alto-falantes de áudio, etc.
Os sistemas operacionais inspirados no Unix, como o Linux, oferecem uma grande variedade de métodos de entrada/saída. Em particular, o método dos descritores de arquivo permite associar dinamicamente números inteiros a canais de dados, para que um processo possa referenciá-los como seus fluxos de dados de entrada/saída.
Os processos padrão do Linux têm três canais de comunicação abertos por padrão: o canal de entrada padrão (na maioria das vezes simplesmente chamado de stdin), o canal de saída padrão (stdout) e o canal de erro padrão (stderr). Os descritores numéricos de arquivo atribuídos a esses canais são 0
para stdin, 1
para stdout e 2
para stderr. Os canais de comunicação também são acessíveis por meio dos dispositivos especiais /dev/stdin
, /dev/stdout
e /dev/stderr
.
Esses três canais de comunicação permitem que os programadores escrevam códigos que lêem e gravam dados sem se preocupar com o tipo de mídia de onde vêm ou para o qual vão. Por exemplo, se um programa precisa de um conjunto de dados como entrada, pode simplesmente solicitar os dados da entrada padrão; será fornecido aquilo que estiver sendo usado como entrada padrão. Da mesma forma, o método mais simples que um programa pode usar para exibir sua saída é escrevê-la na saída padrão. Em uma sessão comum do shell, o teclado é definido como stdin e a tela do monitor como stdout e stderr.
O shell Bash tem a capacidade de reatribuir os canais de comunicação ao carregar um programa. Ele permite, por exemplo, substituir a tela como a saída padrão e usar um arquivo no sistema de arquivos local como stdout.
Redirecionamentos
A reatribuição do descritor de arquivo de um canal no ambiente shell é chamada de redirecionamento. Um redirecionamento é definido por um caractere especial na linha de comando. Por exemplo, para redirecionar a saída padrão de um processo para um arquivo, o símbolo de maior que >
é posicionado no final do comando e seguido pelo caminho até o arquivo que receberá a saída redirecionada:
$ cat /proc/cpuinfo >/tmp/cpu.txt
Por padrão, apenas o conteúdo que chega a stdout é redirecionado. Isso ocorre porque o valor numérico do descritor de arquivo deve ser especificado logo antes do símbolo de maior que e, quando não especificado, o Bash redireciona a saída padrão. Portanto, usar >
é equivalente a usar 1>
(o valor do descritor de arquivo de stdout é 1
).
Para capturar o conteúdo de stderr, o redirecionamento 2>
deve ser usado. A maioria dos programas de linha de comando enviam informações de depuração e mensagens de erro para o canal de erro padrão. É possível, por exemplo, capturar a mensagem de erro gerada por uma tentativa de leitura de um arquivo inexistente:
$ cat /proc/cpu_info 2>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory
Tanto stdout quanto stderr são redirecionados para o mesmo destino com &>
ou >&
. É importante não colocar nenhum espaço ao lado do "e" comercial, caso contrário o Bash o interpretará como uma instrução para executar o processo em segundo plano e não para executar o redirecionamento.
O destino deve ser um caminho para um arquivo gravável, como /tmp/cpu.txt
, ou um descritor de arquivo gravável. O destino de um descritor de arquivo é representado por um "e" comercial seguido pelo valor numérico do descritor de arquivo. Por exemplo, 1>&2
redireciona stdout para stderr. Para fazer o oposto, stderr para stdout, devemos usar 2>&1
.
Embora não seja muito útil, visto que existe uma maneira mais curta de executar a mesma tarefa, é possível redirecionar stderr para stdout e, em seguida, redirecioná-lo para um arquivo. Por exemplo, um redirecionamento para gravar stderr e stdout em um arquivo chamado log.txt
pode ser escrito como >log.txt 2>&1
. No entanto, o principal motivo para redirecionar stderr para stdout é permitir a análise de mensagens de depuração e erro. É possível redirecionar a saída padrão de um programa para a entrada padrão de outro programa, mas não é possível redirecionar diretamente o erro padrão para a entrada padrão de outro programa. Assim, as mensagens do programa enviadas para stderr primeiro precisam ser redirecionadas para stdout a fim de serem lidas pelo stdin de outro programa.
Para simplesmente descartar a saída de um comando, seu conteúdo pode ser redirecionado para o arquivo especial /dev/null
. Por exemplo, >log.txt 2>/dev/null
salva o conteúdo de stdout no arquivo log.txt
e descarta o stderr. O arquivo /dev/null
pode ser escrito por qualquer usuário, mas nenhum dado pode ser recuperado dele, pois não é armazenado em lugar nenhum.
Uma mensagem de erro é apresentada se o destino especificado não for gravável (se o caminho apontar para um diretório ou um arquivo somente leitura) e nenhuma modificação for feita no destino. No entanto, um redirecionamento de saída sobrescreve um destino gravável existente sem pedir nenhuma confirmação. Os arquivos são substituídos pelos redirecionamentos de saída, a menos que a opção noclobber
esteja habilitada no Bash, o que pode ser feito para a sessão atual com o comando set -o noclobber
ou set -C
:
$ set -o noclobber $ cat /proc/cpu_info 2>/tmp/error.txt -bash: /tmp/error.txt: cannot overwrite existing file
Para remover a opção noclobber
da sessão atual, execute set +o noclobber
ou set +C
. Para tornar a opção noclobber
persistente, ela deve ser incluída no perfil Bash do usuário ou no perfil de todo o sistema.
Mesmo com a opção noclobber
habilitada, é possível anexar dados redirecionados ao conteúdo existente. Usamos para isso um redirecionamento escrito com dois símbolos de maior que, >>
:
$ cat /proc/cpu_info 2>>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
No exemplo anterior, a nova mensagem de erro foi anexada à existente no arquivo /tmp/error.txt
. Se o arquivo ainda não existir, ele será criado com os novos dados.
A fonte de dados da entrada padrão de um processo também pode ser reatribuída. O símbolo de menor que <
é usado para redirecionar o conteúdo de um arquivo para o stdin de um processo. Nesse caso, os dados fluem da direita para a esquerda: o descritor reatribuído é considerado como sendo 0 à esquerda do símbolo de menor que e a fonte de dados (um caminho para um arquivo) deve estar à direita do símbolo de menor que. O comando uniq
, como a maioria dos utilitários de linha de comando para processamento de texto, aceita os dados enviados para stdin por padrão:
$ uniq -c </tmp/error.txt 2 cat: /proc/cpu_info: No such file or directory
A opção -c
faz com que o uniq
exiba quantas vezes uma linha repetida aparece no texto. Como o valor numérico do descritor de arquivo redirecionado foi suprimido, o comando de exemplo é equivalente a uniq -c 0</tmp/error.txt
. O uso de um descritor de arquivo diferente de 0
em um redirecionamento de entrada só faz sentido em determinados contextos, porque um programa pode possivelmente solicitar dados dos descritores 3
, 4
, etc. De fato, os programas podem usar qualquer número inteiro maior que 2 como novos descritores de arquivo para entrada/saída de dados. Por exemplo, o código em C a seguir lê dados do descritor de arquivo 3
e simplesmente os reproduz para o descritor 4
:
Note
|
O programa deve gerenciar corretamente esses descritores de arquivo, caso contrário ele pode tentar uma operação inválida de leitura ou gravação e travar. |
#include <stdio.h> int main(int argc, char **argv){ FILE *fd_3, *fd_4; // Open file descriptor 3 fd_3 = fdopen(3, "r"); // Open file descriptor 4 fd_4 = fdopen(4, "w"); // Read from file descriptor 3 char buf[32]; while ( fgets(buf, 32, fd_3) != NULL ){ // Write to file descriptor 4 fprintf(fd_4, "%s", buf); } // Close both file descriptors fclose(fd_3); fclose(fd_4); }
Para testá-lo, salve o código de amostra como fd.c
e compile-o com gcc -o fd fd.c
. Este programa precisa que os descritores de arquivo 3 e 4 estejam disponíveis para poder ler e gravar neles. Como exemplo, o arquivo /tmp/error.txt
criado anteriormente pode ser usado como fonte para o descritor de arquivo 3
e o descritor de arquivo 4
pode ser redirecionado para stdout:
$ ./fd 3</tmp/error.txt 4>&1 cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
Do ponto de vista do programador, o uso de descritores de arquivo evita a obrigação de lidar com a análise de opções (parsing) e com os caminhos do sistema de arquivos. É possível até usar o mesmo descritor de arquivo como entrada e saída. Nesse caso, o descritor de arquivo é definido na linha de comando com os símbolos menor que e maior que, como em 3<>/tmp/error.txt
.
Here Document e Here String
Outra forma de redirecionar a entrada envolve os métodos Here document e Here string. O redirecionamento Here document permite digitar um texto de várias linhas que será usado como o conteúdo redirecionado. Dois símbolos de menor que <<
indicam um redirecionamento de Here document:
$ wc -c <<EOF > How many characters > in this Here document? > EOF 43
À direita dos dois símbolos de menor que <<
está o termo de fim EOF
. O modo de inserção termina assim que for inserida uma linha contendo apenas o termo de fim. Qualquer outro termo pode ser usado como termo de fim, mas é importante não colocar caracteres em branco entre o símbolo de menor que e o termo de fim. No exemplo acima, as duas linhas de texto foram enviadas para o stdin do comando wc -c
, que exibe a contagem de caracteres. Assim como acontece com os redirecionamentos de entrada para arquivos, o stdin (descritor de arquivo 0
) é pressuposto se o descritor de arquivo redirecionado for suprimido.
O método de Here string é muito parecido com o método de Here document, mas para uma linha apenas:
$ wc -c <<<"How many characters in this Here string?" 41
Neste exemplo, a string à direita dos três sinais de menor é enviada para o stdin de wc -c
, que conta o número de caracteres. As strings contendo espaços devem estar entre aspas, senão apenas a primeira palavra será usada como Here string e as restantes serão passadas como argumentos para o comando.
Exercícios Guiados
-
Além dos arquivos de texto, o comando
cat
também pode trabalhar com dados binários, como enviar o conteúdo de um dispositivo de bloco para um arquivo. Usando redirecionamento, como ocat
pode enviar o conteúdo do dispositivo/dev/sdc
para o arquivosdc.img
no diretório atual? -
Qual é o nome do canal padrão redirecionado pelo comando
date 1> now.txt
? -
Ao tentar sobrescrever um arquivo usando redirecionamento, um usuário recebe uma mensagem de erro informando que a opção
noclobber
está habilitada. Como essa opção pode ser desativada para a sessão atual? -
Qual será o resultado do comando
cat <<.>/dev/stdout
?
Exercícios Exploratórios
-
O comando
cat /proc/cpu_info
exibe uma mensagem de erro porque/proc/cpu_info
não existe. Para onde o comandocat /proc/cpu_info 2>1
redireciona a mensagem de erro? -
Ainda será possível descartar o conteúdo enviado para
/dev/null
se a opçãonoclobber
estiver habilitada para a sessão de shell atual? -
Sem usar
echo
, como o conteúdo da variável$USER
poderia ser redirecionado para o stdin do comandosha1sum
? -
O kernel do Linux mantém links simbólicos em
/proc/PID/fd/
para cada arquivo aberto por um processo, onde PID é o número de identificação do processo correspondente. Como o administrador do sistema poderia usar esse diretório para verificar a localização dos arquivos de log abertos pelonginx
, supondo que seu PID é1234
? -
É possível fazer cálculos aritméticos usando apenas comandos internos do shell, mas cálculos de ponto flutuante requerem programas específicos, como o
bc
(basic calculator). Com obc
é possível até mesmo especificar o número de casas decimais, com o parâmetroescala
. No entanto, obc
aceita operações apenas por meio de sua entrada padrão, geralmente inserida no modo interativo. Usando uma Here string, como a operação de ponto flutuantescale=6; 1/3
pode ser enviada para a entrada padrão debc
?
Resumo
Esta lição cobre métodos para executar um programa redirecionando seus canais de comunicação padrão. Os processos do Linux usam esses canais padrão como descritores de arquivo genéricos para ler e gravar dados, tornando possível transferi-los arbitrariamente para arquivos ou dispositivos. A lição demonstra as seguintes etapas:
-
O que são descritores de arquivos e qual seu papel no Linux.
-
Os canais de comunicação padrão em todos os processos: stdin, stdout e stderr.
-
Como executar corretamente um comando usando redirecionamento de dados, tanto na entrada quanto na saída.
-
Como usar Here Documents e Here Strings nos redirecionamentos de entrada.
Os comandos e procedimentos abordados foram:
-
Operadores de redirecionamento:
>
,<
,>>
,<<
,<<<
. -
Comandos
cat
,set
,uniq
ewc
.
Respostas aos Exercícios Guiados
-
Além dos arquivos de texto, o comando
cat
também pode trabalhar com dados binários, como enviar o conteúdo de um dispositivo de bloco para um arquivo. Usando redirecionamento, como ocat
pode enviar o conteúdo do dispositivo/dev/sdc
para o arquivosdc.img
no diretório atual?$ cat /dev/sdc > sdc.img
-
Qual é o nome do canal padrão redirecionado pelo comando
date 1> now.txt
?Standard output ou stdout
-
Ao tentar sobrescrever um arquivo usando redirecionamento, um usuário recebe uma mensagem de erro informando que a opção
noclobber
está habilitada. Como essa opção pode ser desativada para a sessão atual?set +C
ouset +o noclobber
-
Qual será o resultado do comando
cat <<.>/dev/stdout
?O Bash entrará no modo de entrada Heredoc e sairá quando um ponto final aparecer sozinho em uma linha. O texto digitado será redirecionado para stdout (impresso na tela).
Respostas aos Exercícios Exploratórios
-
O comando
cat /proc/cpu_info
exibe uma mensagem de erro porque/proc/cpu_info
não existe. Para onde o comandocat /proc/cpu_info 2>1
redireciona a mensagem de erro?Para um arquivo chamado
1
no diretório atual. -
Ainda será possível descartar o conteúdo enviado para
/dev/null
se a opçãonoclobber
estiver habilitada para a sessão de shell atual?Sim.
/dev/null
é um arquivo especial, não afetado pornoclobber
. -
Sem usar
echo
, como o conteúdo da variável$USER
poderia ser redirecionado para o stdin do comandosha1sum
?$ sha1sum <<<$USER
-
O kernel do Linux mantém links simbólicos em
/proc/PID/fd/
para cada arquivo aberto por um processo, onde PID é o número de identificação do processo correspondente. Como o administrador do sistema poderia usar esse diretório para verificar a localização dos arquivos de log abertos pelonginx
, supondo que seu PID é1234
?Emitindo o comando
ls -l /proc/1234/fd
, que exibirá os destinos de cada link simbólico no diretório. -
É possível fazer cálculos aritméticos usando apenas comandos internos do shell, mas cálculos de ponto flutuante requerem programas específicos, como o
bc
(basic calculator). Com obc
é possível até mesmo especificar o número de casas decimais, com o parâmetroescala
. No entanto, obc
aceita operações apenas por meio de sua entrada padrão, geralmente inserida no modo interativo. Usando uma Here string, como a operação de ponto flutuantescale=6; 1/3
pode ser enviada para a entrada padrão debc
?$ bc <<<"scale=6; 1/3"