13

I'd like to edit my .bashrc so that every command executed on the shell is piped to something, for example:

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

I have managed something rather similar, but not entirely:

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

It is not the desired result, as it only happens after exiting the current shell.

It's mainly for fun/learning purposes.

  • Using the -n flag to cowsay is useful; it makes it preserve whitespace. – wjandrea Jun 1 '18 at 20:00
12

You can adapt your method a bit. Instead of piping to cowsay directly, read output till a delimiting character, send that output to cowsay, then print that character after every command:

exec > >(while IFS= read -d '' -r line; do if [[ -n $line ]]; then echo; printf "%s\n" "$line" | cowsay; fi; done)
PROMPT_COMMAND='printf "\0"'

Here, I'm using the ASCII NUL character. You can use something else that's unlikely to appear in command output.

This will print after the prompt, so the output will be ugly:

$ 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 "\0"'
$ ls
$
 ______________________________________
/ Desktop Documents Downloads Music    \
| Pictures Public Templates Videos
\ examples.desktop                     /
 --------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

$ echo foo
$
 ______
< foo  >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Note that this will break any command which tries complex output or has a text user interface (think command line editors, pagers, etc.).

Assuming you already know what exec > >(...) does, the part in the process substitution is:

  • while IFS= read -d '' -r line; do ... done: this is a fairly common idiom for reading data delimited by the ASCII NUL character:

    • IFS= sets the IFS to the empty string, which disables field splitting
    • -r prevents read from treating \ in the input specially (so, \n for example, is read as \n and not converted to the newline character).
    • -d '' is the way to tell read to read until the NUL character

    So the whole thing loops over input in NUL-delimited sections, while preserving the contents of the input as much as possible.

  • if [[ -n $line ]]; then ... fi; done - only act if the input read so far is not empty.
  • echo; printf "%s\n" "$line" | cowsay; - print a leading empty line, so that the cowsay output doesn't clash with the prompt, and then send the input read so far to cowsay. printf is more reliable and safer than echo.
  • 1
    As my prompt has a linebreak in it the cowsay output does clash with the second part of it – maybe also set the prompt to something non-distracting? – dessert Dec 8 '17 at 12:29
16

You can trap and abuse bash's DEBUG signal:

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

Example run

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

However, this will still execute the command afterwards. Thanks to ilkkachu I found a way around that:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.