6

I would like to be able to pipe all bash terminal commands through a certain command (for no good reason other than to play a prank on someone). I just want to pipe the stdout of any executed command into a predetermined program without doing anything special.

For example: If that predetermined program was cowsay

echo "Hello World"

should output

 _____________
< Hello World >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

How can I achieve this? (Some of the fun programs I'd like to use to play pranks on others include rev, cowsay, and lolcat)

vikarjramun
  • 413
  • 1
  • 7
  • 20

1 Answers1

7
exec > >(COMMAND)

Where COMMAND is rev, lolcat or other. This won't work with cowsay.

E.g.

bash-4.3$ exec > >(rev)
bash-4.3$ echo hello
olleh

Explanation:

  • exec normally replaces the current shell with another process, but if you just give it a redirection like in this case, the redirection will take place for the current shell.
  • > redirect stdout
  • >(COMMAND) input into COMMAND

Note that if you have a PROMPT_COMMAND, you should direct it to stderr to avoid the redirected stdout.

wjandrea
  • 14,109
  • 4
  • 48
  • 98
  • Thanks for the explanation, two questions: 1. It works great for tools like lolcat or rev which operate on a line by line basis. But for cowsay, it waits until I press ctrl-d, then outputs everything. How can we make it add an EOF to each command, and spawn a new cowsay each time? – vikarjramun Jun 01 '18 at 17:12
  • @vikarjramun Yeah, I'm having the same problem. Must be a buffering thing. I'm researching it now – wjandrea Jun 01 '18 at 17:13
  • 2
    @wjandrea Perhaps the [`unbuffer`](http://expect.sourceforge.net/example/unbuffer.man.html) command from `expect` could help. – PerlDuck Jun 01 '18 at 17:16
  • @wjandrea my first thought was `xargs cowsay` but that didn't work. Maybe `xargs bash -c "echo {} | cowsay"`? I'll try that and see. – vikarjramun Jun 01 '18 at 17:30
  • @PerlDuck I couldn't get it to work. I tried it with and without the `-p` flag. – wjandrea Jun 01 '18 at 18:57
  • The closest I've come yet is using `exec > >(while read line; do cowsay <<<"$line"; done)` or `exec > >(xargs -l cowsay)`, but it prints after the next prompt has already come up. – wjandrea Jun 01 '18 at 19:01
  • I don't know about the `-p` flag but it was just a guess anyway. Maybe the bash itself needs to be run via `unbuffer` but I wouldn't dare that. Perhaps we should accept that we cannot tame the cow. – PerlDuck Jun 01 '18 at 19:01
  • Well, if the only problem left is the prompt coming up to early, let's just delay the prompt... `PS1="\$(sleep 1)$PS1"` ;-) Tested it, works fine with `exec > >(xargs -l cowsay)` – Lienhart Woitok Jun 01 '18 at 19:41
  • @LienhartWoitok I tried that too, but with a command that produces a lot of output lines, like `ls`, it doesn't work. – wjandrea Jun 01 '18 at 19:54