How to pipe stdout while keeping it on screen ? (and not to a output file)

BashShellPipeOutputStdout

Bash 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongentooboontooView Question on Stackoverflow
Solution 1 - BashjlliagreView Answer on Stackoverflow
Solution 2 - BashbmkView Answer on Stackoverflow
Solution 3 - BashJanView Answer on Stackoverflow
Solution 4 - Bashuser51527View Answer on Stackoverflow
Solution 5 - BashMichael MartinezView Answer on Stackoverflow