Redirecionamentos no Bash
Curso de Bash
Seção do curso sobre redirecionamentos.
Entrada padrão (ou o acrônimo em inglês stdin
) é um dos 3 canais de comunicação padrão entre um processo e seu ambiente. Nesse canal os dados são originados no ambiente e trafegam em destino ao processo.
É dito que um programa lê dados da entrada padrão quando digitamos os caracteres em um terminal (ou outro ambiente) e tais caracteres são buscados pelo programa.
Saída padrão (stout
) é o destino dos dados produzidos por um processo. Tipicamente conectado ao terminal.
Erro padrão (stderr
) também é um canal de saída, mas é utilizado apenas para dar saída às mensagens de erro.
Conteúdo
Origem da entrada padrão
A entrada padrão é representada pelo descritor de arquivo 0 (do inglês, file descriptor), que é um link com o caminho /proc/<PID>/fd/0
, onde <PID>
é substituído pelo PID do processo.
Por padrão esse link aponta para um arquivo que representa um terminal.
O comando tty
exibe o nome do arquivo que representa o terminal.
tty
/dev/pts/1
Abaixo podemos verificar que a entrada padrão do processo com PID 5311 está conectada ao terminal /dev/pts/1
.
ls -l /proc/5311/fd/0
lrwx------ 1 caio caio 64 Dec 6 10:00 /proc/5311/fd/0 -> /dev/pts/1
Lendo dados da entrada padrão
-
pode ser utilizado como argumento para que o comando suportado leia dados da entrada padrão.
Vários dos comandos, especialmente para processamento de texto, aceitam -
como argumento geralmente no lugar que seria de um arquivo de texto.
O argumento -
facilita a entrada de dados já que linhas podem ser digitadas diretamente no terminal, evitando assim a criação de um arquivo.
-
é opcional quando nenhum outro argumento é especificado. shuf
, sort
, cat
, tac
, head
, tail
, wc
são exemplos de comandos que leem da entrada padrão quando nenhum argumento é especificado ou quando -
é especificado.
shuf
Logo após digitarmos o nome do comando e pressionarmos Enter
, o cursor é movido para a linha seguinte. A partir daí qualquer caractere digitado fará parte da entrada, incluíndo o caractere novalinha
que é inserido quando pressionamos Enter
.
Ctrl
+ D
envia um caractere chamado EOF
ou End of File
(fim do arquivo, em português) para indicar o término da entrada. Ctrl
+ D
deve ser pressionado uma vez quando o cursor está em uma linha vazia e duas vezes quando há caracteres na linha.
Assim que terminada a entrada de dados o comando é executado.
Na entrada abaixo Ctrl
+ D
é representado por ^D
apenas para ilustrar o conceito, o atalho não é exibido.
Z
D
F
H
^D
A saída inicia na linha em que Ctrl
+ D
foi pressionado.
H
Z
F
D
-
é necessário quando há outros argumentos. >
abaixo indica as linhas digitadas pois a entrada e saída estão intercaladas.
head -n 2 /etc/hosts -
==> /etc/hosts <==
127.0.0.1 localhost
127.0.1.1 FX505DV
==> standard input <==
> A
A
> B
B
Redirecionamento de entrada
É representado por <
e redireciona a entrada padrão para o arquivo (ou outro recurso) à sua direita.
O arquivo letras
é criado abaixo contendo as letras de A até D, cada uma em uma linha.
printf "%s\n" {A..D} > letras
A entrada padrão de wc
é redirecionada para o arquivo letras
.
< letras wc
4 4 8
O uso abaixo, com o redirecionamento à direita do comando, tem efeito idêntico e é a forma mais comum.
wc < letras
4 4 8
Here Documents
Tipo de redirecionamento da entrada padrão que permite especificar as linhas diretamente no terminal.
É especificado com o operador << <palavra>
onde <palavra>
é a sequência que determina o fim da entrada.
Difere de -
pois a entrada é terminada com <palavra>
ao invés de Ctrl
+ D
.
O operador <<
abaixo determina que a linha contendo fim
, sem nenhum caractere o precedendo ou sucedendo, encerra o here-document.
tr -d "[:digit:]" << fim
A1B2
C3
fim 4
fim
Saída
AB
C
fim
Expansão de variável, substituição de comando e expansão aritmética também são interpretadas (conceitos abordados em capítulos futuros).
tac << ultima_linha
Número aleatório $RANDOM
Diretório atual $(pwd)
Exponenciação $(( 2 ** 10))
ultima_linha
Saída
Exponenciação 1024
Diretório atual /home/caio
Número aleatório 315
Para evitar qualquer expansão basta colocar o delimitador entre ""
ou ''
. Contudo o delimitador da última linha da entrada deve aparecer sem as aspas.
tac << "ultima_linha"
Exponenciação $(( 2 ** 10))
Diretório atual $(pwd)
Número aleatório $RANDOM
ultima_linha
⚠️ Ctrl
+ D
na entrada padrão ocasiona um aviso na saída apesar de processar todas as linhas posteriores.
tr "[:lower:]" "[:upper:]" << termino
> Uma linha
> ^D
> bash: warning: here-document at line 43 delimited by end-of-file (wanted `termino')
UMA LINHA
Here Strings
O operador <<<
realiza o redirecionamento da entrada padrão para a palavra à sua direita. A palavra é chamada de here_string.
wc <<< A
1 1 2
❌ Especificar mais de um operando é um erro. Bash interpreta as palavras adicionais como argumentos.
wc <<< A B C
wc: B: No such file or directory
wc: C: No such file or directory
0 0 0 total
Aspas podem ser utilizadas para esse fim.
wc <<< "A B C"
1 3 6
Aspas também podem ser utilizadas para inserir novalinha
na here-string.
wc -l <<< "A
B C"
2
Expansão de variável, substituição de comando e expansão aritmética são interpretadas na here-string. Conceitos abordados em capítulos futuros.
cat <<< "$BASH_VERSION $(date) $((~-2))"
5.0.17(1)-release Sun 06 Dec 2020 17:25:30 -03 1
Redirecionamento da saída
Assim como a entrada padrão, a saída padrão e o erro padrão são tipicamente conectados ao terminal.
tty
/dev/pts/1
Saída padrão tem o descritor de arquivo 1 e o erro padrão 2.
ls -l /proc/5080/fd
lrwx------ 1 caio caio 64 Dec 7 09:25 0 -> /dev/pts/1
lrwx------ 1 caio caio 64 Dec 7 09:25 1 -> /dev/pts/1
lrwx------ 1 caio caio 64 Dec 7 09:25 2 -> /dev/pts/1
O operador de redirecionamento >
redireciona a saída do comando para o arquivo à sua direita. Caso o arquivo não exista, é criado. Caso já exista é sobrescrito.
O comando date
tem sua saída redirecionada para o arquivo data_e_hora
e então o arquivo tem seu conteúdo exibido por head
.
date > data_e_hora
head data_e_hora
Mon 07 Dec 2020 10:22:08 -03
Caso utilizemos o arquivo novamente como operando de >
, ele será sobrescrito.
ps > data_e_hora
head data_e_hora
PID TTY TIME CMD
5080 pts/1 00:00:00 bash
9766 pts/1 00:00:00 ps
Já o operador >>
insere a saída ao fim do operando (o arquivo à sua direita). Caso o arquivo não exista, é criado.
whoami >> data_e_hora
PID TTY TIME CMD
5080 pts/1 00:00:00 bash
9766 pts/1 00:00:00 ps
caio
Além de arquivos comuns, é possível utilizar arquivos especiais como abaixo, em que a saída é redirecionada para outro terminal.
groups > /dev/pts/2
Saída no terminal /dev/pts/2
, logo após o prompt.
caio@FX505DV:~/temp$ caio adm cdrom sudo dip plugdev lpadmin lxd sambashare
Redirecionamento de erro padrão
2>
e 2>>
redirecionam as mensagems de erro como os operadores >
e >>
fazem com a saída regular.
ls /inexistente 2> errors
tail erros
ls: cannot access '/inexistente': No such file or directory
É possível ignorar as mensagens de erro ou mesmo a saída padrão redirecionando-as para /dev/null
.
mkdir -v . .. um_dir 2> /dev/null
mkdir: created directory 'um_dir'
Mais de um redirecionamento pode ser especificado para um mesmo comando.
Abaixo cat
envia para a saída padrão (redirecionada para o meu_arquivo
) o que foi lido da entrada padrão (redicionada para a Uma linha
) e tail
exibe o conteúdo do recém criado meu_arquivo
.
cat > meu_arquivo <<< "Uma linha"
tail meu_arquivo
Uma linha
Redirecionando erro e saída padrão simultaneamente
O operador &>
redireciona ambas as saídas (saída padrão e erro padrão) para o arquivo à direita. O arquivo é criado caso não exista e sobrescrito caso exista, como ocorre com >
e 2>
.
mkdir -v .. . dir1 dir2 &> saidas
head saidas
mkdir: cannot create directory ‘..’: File exists
mkdir: cannot create directory ‘.’: File exists
mkdir: created directory 'dir1'
mkdir: created directory 'dir2'
Verificando os descritores de arquivo
No primeiro terminal inicie nova sessão de Bash redirecionando sua saída e erro padrão para o arquivo historico
.
bash &> historico
No segundo terminal execute tail -F
para acompanhar as inserções de linha.
tail -F historico
De volta ao primeiro, execute o comando abaixo para obter o PID da sessão de Bash.
echo $$
PID exibido na saída no segundo terminal.
29316
Ainda no primeiro terminal, exiba os descritores de arquivo para a sessão de Bash.
ls -l /proc/29316/fd
Saída no segundo.
29316
total 0
lrwx------ 1 caio caio 64 Dec 7 15:26 0 -> /dev/pts/1
l-wx------ 1 caio caio 64 Dec 7 15:26 1 -> /home/caio/historico
l-wx------ 1 caio caio 64 Dec 7 15:26 2 -> /home/caio/historico
Ctrl
+ D
no primero para sair da sessão de Bash e Ctrl
+ C
no segundo para interromper tail
.