Como eu canalizo cada comando dado ao shell?

13

Eu gostaria de editar meu .bashrc para que todos os comandos executados no shell sejam canalizados para alguma coisa, por exemplo:

 $ sudo apt update
  _________________
< sudo apt update >
 -----------------
    \   ^__^
     \  (oo)\_______
        (__)\       )\/\
            ||----w |
            ||     ||

Eu consegui algo bastante semelhante, mas não inteiramente:

$ bash
$ exec > >(cowsay)
$ echo "Hello AU!"
$ exit
 _______
< Hello AU! >
 -------
    \   ^__^
     \  (oo)\_______
        (__)\       )\/\
            ||----w |
            ||     ||

Não é o resultado desejado, pois só acontece depois de sair do shell atual.

É principalmente para fins de diversão / aprendizagem.

    
por M. Becerra 08.12.2017 / 11:49

2 respostas

12

Você pode adaptar seu método um pouco. Em vez de canalizar para cowsay diretamente, leia a saída até um caractere delimitador, envie essa saída para cowsay e imprima esse caractere após cada comando:

exec > >(while IFS= read -d '' -r line; do if [[ -n $line ]]; then echo; printf "%s\n" "$line" | cowsay; fi; done)
PROMPT_COMMAND='printf "
$ export LC_ALL=C
$ exec > >(while IFS= read -d '' -r line; do if [[ -n $line ]]; then echo; printf "%s\n" "$line" | cowsay; fi; done)
$ PROMPT_COMMAND='printf "
exec > >(while IFS= read -d '' -r line; do if [[ -n $line ]]; then echo; printf "%s\n" "$line" | cowsay; fi; done)
PROMPT_COMMAND='printf "
$ export LC_ALL=C
$ exec > >(while IFS= read -d '' -r line; do if [[ -n $line ]]; then echo; printf "%s\n" "$line" | cowsay; fi; done)
$ PROMPT_COMMAND='printf "%pre%"'
$ ls
$
 ______________________________________
/ Desktop Documents Downloads Music    \
| Pictures Public Templates Videos
\ examples.desktop                     /
 --------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

$ echo foo
$
 ______
< foo  >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
"'
"' $ ls $ ______________________________________ / Desktop Documents Downloads Music \ | Pictures Public Templates Videos \ examples.desktop / -------------------------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || $ echo foo $ ______ < foo > ------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || ||
"'

Aqui, estou usando o caractere ASCII NUL. Você pode usar outra coisa que provavelmente não aparece na saída do comando.

Isso será impresso após o prompt, então a saída será feia:

%pre%

Observe que isso quebrará qualquer comando que tente uma saída complexa ou tenha uma interface de usuário de texto (imagine editores de linha de comando, pagers, etc.).

Supondo que você já saiba o que o exec > >(...) faz, a parte na substituição do processo é:

  • while IFS= read -d '' -r line; do ... done : este é um idioma bastante comum para ler dados delimitados pelo caractere ASCII NUL:

    • IFS= define o IFS para a cadeia vazia, o que desativa a divisão de campos
    • -r impede que read trate \ na entrada especialmente (por isso, \n , por exemplo, é lido como \n e não convertido para o caractere de nova linha).
    • -d '' é a maneira de informar read para ler até o caractere NUL

    Assim, a coisa toda faz um loop sobre a entrada em seções delimitadas por NUL, enquanto preserva o conteúdo da entrada o máximo possível.

  • if [[ -n $line ]]; then ... fi; done - só age se a entrada lida até agora não estiver vazia.
  • echo; printf "%s\n" "$line" | cowsay; - imprime uma linha vazia inicial, de modo que a saída do cowsay não colida com o prompt, e então envia a entrada lida até agora para o cowsay. printf é mais confiável e seguro que echo .
por muru 08.12.2017 / 12:15
16

Você pode trap e abusar do sinal DEBUG do bash:

trap 'bash -c "$BASH_COMMAND" | cowsay' DEBUG

Exemplo de execução

$ trap 'bash -c "$BASH_COMMAND" | cowsay' DEBUG
$ echo "AU is awesome!"
 __________________
< AU is awesome! >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
AU is awesome!

No entanto, isso ainda executará o comando depois. Graças a ilkkachu eu encontrei uma maneira de contornar isso:

$ shopt -s extdebug
$ trap 'bash -c "$BASH_COMMAND" | cowsay; false' DEBUG
$ echo "AU is awesome!"
 __________________
< AU is awesome! >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
    
por dessert 08.12.2017 / 12:10