Celeb Glow
updates | March 27, 2026

How do I pipe each command given to the shell?

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.

1

2 Answers

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 | || ||
0

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

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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