How to pipe stdout while keeping it on screen ? (and not to a output file)
BashShellPipeOutputStdoutBash Problem Overview
I would like to pipe standard output of a program while keeping it on screen.
With a simple example (echo
use here is just for illustration purpose) :
$ echo 'ee' | foo
ee
<- the output I would like to see
I know tee could copy stdout to file but that's not what I want.
$ echo 'ee' | tee output.txt | foo
I tried
$ echo 'ee' | tee /dev/stdout | foo
but it does not work since tee output to /dev/stdout
is piped to foo
Bash Solutions
Solution 1 - Bash
Here is a solution that works at on any Unix / Linux implementation, assuming it cares to follow the POSIX
standard. It works on some non Unix environments like cygwin
too.
echo 'ee' | tee /dev/tty | foo
Reference: The Open Group Base Specifications Issue 7 IEEE Std 1003.1, 2013 Edition, §10.1:
> /dev/tty > > Associated with the process group of that process, if any. It is > useful for programs or shell procedures that wish to be sure of > writing messages to or reading data from the terminal no matter how > output has been redirected. It can also be used for applications that > demand the name of a file for output, when typed output is desired and > it is tiresome to find out what terminal is currently in use. In each process, a synonym for the controlling terminal
Some environments like Google Colab have been reported not to implement /dev/tty
while still having their tty
command returning a usable device. Here is a workaround:
tty=$(tty)
echo 'ee' | tee $tty | foo
or with an ancient Bourne shell:
tty=`tty`
echo 'ee' | tee $tty | foo
Solution 2 - Bash
Another thing to try is:
echo 'ee' | tee >(foo)
The >(foo)
is a process substitution.
Edit:
To make a bit clearer, (.) here start a new child process to the current terminal, where the output is being redirected to.
echo ee | tee >(wc | grep 1)
# ^^^^^^^^^^^^^^ => child process
Except that any variable declarations/changes in child process do not reflect in the parent, there is very few of concern with regard to running commands in a child process.
Solution 3 - Bash
Try:
$ echo 'ee' | tee /dev/stderr | foo
If using stderr is an option, of course.
Solution 4 - Bash
Access to "/dev/stdout" is denied on some systems, but access to the user terminal is given by "/dev/tty".
Using "wc" for "foo", the above examples work OK (on linux, OSX, etc.) as:
>% echo 'Hi' | tee /dev/tty | wc Hi 1 1 3
To add a count at the bottom of a list of matching files, I use something like:
% ls [A-J]* | tee /dev/tty | wc -l
To avoid having to remember all this, I define aliases:
% alias t tee /dev/tty
% alias wcl wc -l
so that I can simply say:
% ls [A-J]* | t | wcl
> POSTSCRIPT: For the younger set, who might titter at its pronunciation as "titty", I might add that "tty" was once the common > abbreviation for a "teletype" terminal, which used a roll of yellow > paper and had round keys that often stuck.
Solution 5 - Bash
first you need to figure out the terminal associated with your screen (or whichever screen you want the output to display on):
tty
then you can tee the output to that terminal and pipe the other copy through your foo program:
echo ee | tee /dev/pty/2 | foo