How do I pipe each command given to the shell?

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

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.