Pipelines e Listas de comandos no Bash
Curso de Bash
Seção do curso sobre pipelines e lista de comandos.
Conteúdo
Pipelines
Pipelines (|
) permitem que a saída padrão de um processo seja conectada à entrada padrão de um outro.
Com isso as Pipelines evitam a tarefa intermediária de salvar a saída de um processo em arquivo para depois enviar esse arquivo para a entrada de outro processo.
P. ex., abaixo, lista com todos os processos é salva no arquivo processos
e então a quantidade de linhas 1 de processos
é contada.
ps -ef > processos
wc -l < processos
Utilizando |
, temos o mesmo resultado, porém sem o intermédio do arquivo.
ps -ef | wc -l
308
Aqui listamos todos os usuários, filtramos as linhas terminando em bash$
, cortamos a primeira coluna e exibimos no paginador less
.
getent passwd | grep "bash$" | cut -d ":" -f 1 | less
root
caio
usuario
1 tecnicamente a quantidade de caracteres novalinha
.
Duplicando a saída com tee
tee
lê da entrada padrão e escreve tanto na saída padrão e quanto nos arquivos enviados como argumento.
tee arq1 arq2 <<< "Uma linha qualquer"
Uma linha qualquer
Ambos os arquivos conterão o mesmo conteúdo.
head arq2 arq1
==> arq2 <==
Uma linha qualquer
==> arq1 <==
Uma linha qualquer
tee
também é útil como comando intermediário em uma pipeline já que pode escrever em um arquivo os resultados até então processados e também escrever na saída padrão.
No exemplo abaixo, ls
exibe lista de arquivos/diretórios ordenados pela data de modificação, primeiro os mais recentes. grep
remove as linhas que não representam arquivos. tee
escreve a lista de arquivos em configs
e na saída padrão. head
então exibe os 5 arquivos modificados mais recentemente.
ls -lt /etc | grep "^-" | tee configs | head -n 5
-rw-r--r-- 1 root root 138 Dec 6 18:28 shells
-rw-r--r-- 1 root root 106581 Dec 6 10:01 ld.so.cache
-rw-r--r-- 1 root root 57160 Dec 6 10:01 mailcap
-rw-r--r-- 1 root root 3216 Dec 3 22:12 passwd
-rw-r----- 1 root shadow 1748 Dec 3 22:09 shadow
Da entrada padrão para argumento com xargs
Alguns comandos não leem dados da entrada padrão o que os não tornaria candidatos a serem utilizados em pipelines sem xargs
.
xargs
recebe um comando como argumento e todas as linhas lidas na entrada padrão de xargs
são concatenadas em uma só linha que então é definida como argumento para aquele comando.
O arquivo diretorios
é criado abaixo.
printf "%s\n" um_dir outro_dir "dir ferente" > diretorios
head diretorios
⚠️ Note que há um caractere de espaço
entre dir
e ferente
na última linha do arquivo , então cada um deles acaba sendo um argumento diferente para o comando no argumento de xargs
.
um_dir
outro_dir
dir ferente
Sem argumento, xargs
executa o comando echo
.
tail diretorios | xargs
um_dir outro_dir dir ferente
Abaixo xargs
executa mkdir -v
com os argumentos um_dir outro_dir dir ferente
.
tail diretorios | xargs mkdir -v
mkdir: created directory 'um_dir'
mkdir: created directory 'outro_dir'
mkdir: created directory 'dir'
mkdir: created directory 'ferente'
No exemplo abaixo grep
escreve na saída as linhas que não iniciam com #
. xargs
executa realpath
com a concatenção das linhas oriundas de grep
como argumento. realpath
escreve na saída a resolução de cada um dos caminhos especificados como argumento. tee
escreve na saída e no arquivo shells
.
grep "^[^#]" /etc/shells | xargs realpath | tee shells
/usr/bin/dash
/usr/bin/bash
/usr/bin/bash
/usr/bin/bash
/usr/bin/bash
/usr/bin/dash
/usr/bin/dash
A opção -I
de xargs
permite especificar uma sequência de caracteres que identificará onde os argumentos vindos da entrada serão inseridos no comando.
Abaixo sort -u
escreve na saída as linhas que são únicas. xargs
executa cp
substituindo %
pela concatenação das linhas lidas na entrada. cp
copia cada argumento para .
(diretório atual).
sort -u shells | xargs -I % cp -v % .
'/usr/bin/bash' -> './bash'
'/usr/bin/dash' -> './dash'
Lista de comandos
Comandos podem ser especificados em sequência em uma mesma linha. Nas próximas subseções abordaremos os operadores necessários.
Execução condicional de comando com &&
&&
apenas executa o comando à sua direita caso o comando à sua esquerda tenho finalizado com sucesso.
mkdir
cria o diretóio e então ls
lista o diretório.
mkdir diretorio && ls -ld diretorio
drwxrwxr-x 2 caio caio 4096 Dec 9 14:55 diretorio
Como mkdir
não pôde criar o diretório, ls
não foi executado.
mkdir . && ls .
mkdir: cannot create directory ‘.’: File exists
Múltiplos &&
podem ser especificados.
mkdir diretorio2 && cd diretorio2 && pwd
/home/caio/diretorio2
Execução condicional de comando com ||
||
executa o comando à direita apenas se o comando da esquerda terminou com erros.
ls
não pôde listar seu_dir
então mkdir
cria o diretório seu_dir
.
ls seu_dir || mkdir -v seu_dir
ls: cannot access 'seu_dir': No such file or directory
mkdir: created directory 'seu_dir'
mv
renomeou o diretório de seu_dir
para teu_dir
e então cp
não foi executado.
mv -v seu_dir teu_dir || cp -v seu_dir teu_dir
renamed 'seu_dir' -> 'teu_dir'
Múltiplos ||
podem ser especificados.
No exemplo abaixo, mkdir
cria o diretório dir_ou_arq
, então touch
e ls
não são executados.
mkdir -v dir_ou_arq || touch dir_ou_arq || ls -ld dir_ou_arq
mkdir: created directory 'dir_ou_arq'
Lista de comandos com ;
;
permite que múltiplos comandos sejam executados independentemente na mesma linha.
whoami; pwd; date
caio
/home/caio
Wed 09 Dec 2020 16:06:36 -03
Ainda que um dos comandos resulte em falha, os outros são executados normalmente.
whoiam; pwd; date
whoiam: command not found
/home/caio
Wed 09 Dec 2020 16:22:44 -03
Redirecionamentos se limitam às entradas/saídas dos comandos especificados.
Como abaixo, em que apenas a saída de date
é escrita no arquivo data_e_hora
.
whoami; head <<< "ABC"; date >> data_e_hora
caio
ABC
Agrupando comandos com {}
Os comandos listados entre {}
são executados como uma unidade. Funcionalidade especialmente útil para redirecionar a saída conjunta dos comandos.
Assim como com ;
, os comandos são executados sequencialmente, ou seja, o segundo comando apenas é executado quando o primeiro termina.
{
deve ser seguida de espaço
. Todos os comandos, incluindo o último, devem terminar com ;
.
{ whoami; pwd; date; } >> conjunto
head conjunto
caio
/home/caio
Wed 09 Dec 2020 16:18:06 -03
❌ Deixar de inserir o caractere espaço
depois de {
é um erro.
{whoami; pwd; date;}
bash: syntax error near unexpected token `}'
❌ Outro erro é deixar de inserir ;
no último comando. Na situação abaixo Bash interpreta o último comando como date}
, ignora o caractere novalinha
inserido por Enter
/↵
e aguarda por }
para fechar o grupo.
{ whoami; pwd; date}
>
Ao inserir }
, os comandos são executados, com execeção do último (date}
), que inexiste.
> }
caio
/home/caio
Command 'date}' not found, did you mean:
command 'date' from deb coreutils (8.32-3ubuntu1)
time
executa o grupo de comandos e então exibe o tempo passado entre o ínicio e o fim da execução do grupo.
time { whoami; pwd; date; }
caio
/home/caio
Wed 09 Dec 2020 16:28:32 -03
real 0m0.003s
user 0m0.003s
sys 0m0.000s
Já que é tratado como se fosse um só comando, {}
pode ser utilizado entre ||
e &&
.
No exemplo abaixo, ls
gera um erro pois dir
não existe, então o grupo de comandos é executado. No grupo, mkdir
cria o diretório dir
e rm
falha ao excluir o diretório atual (.
) e por falhar, ps
não é executado.
ls dir || { mkdir -v dir; rm .; } && ps
ls: cannot access 'dir': No such file or directory
mkdir: created directory 'dir'
rm: cannot remove '.': Is a directory