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 -l308Aqui 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 | lessroot
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 qualquerAmbos 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 | xargsum_dir outro_dir dir ferenteAbaixo xargs executa mkdir -v com os argumentos um_dir outro_dir dir ferente.
tail diretorios | xargs mkdir -vmkdir: 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 diretoriodrwxrwxr-x 2 caio caio 4096 Dec 9 14:55 diretorioComo mkdir não pôde criar o diretório, ls não foi executado.
mkdir . && ls .mkdir: cannot create directory ‘.’: File existsMúltiplos && podem ser especificados.
mkdir diretorio2 && cd diretorio2 && pwd/home/caio/diretorio2Execuçã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_dirls: 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_dirrenamed '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_arqmkdir: created directory 'dir_ou_arq'Lista de comandos com ;
; permite que múltiplos comandos sejam executados independentemente na mesma linha.
whoami; pwd; datecaio
/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; datewhoiam: 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_horacaio
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 .; } && psls: cannot access 'dir': No such file or directory
mkdir: created directory 'dir'
rm: cannot remove '.': Is a directory