314

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

1
  • 9
    Note that echo 'ee' | tee /dev/stderr works though, so if your "on screen" requirement is satisfied by stderr too, that'll do. Commented Mar 26, 2016 at 23:49

5 Answers 5

431

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 
Sign up to request clarification or add additional context in comments.

24 Comments

@AsyaKamsky The question is about processes which are outputting on the screen. This rules out cron jobs which are detached from any screen in the first place.
@static_rtti Why are you ignoring year after year my replies to your comment?
@PaulBissex /dev/tty is a mandatory Unix device. Are you running in a BSD jail?
@PaulBissex That's either an implementation or a configuration bug. Is /dev mounted? What shows "ls -l /dev/tty /dev/tty* /dev"? See lists.freebsd.org/pipermail/freebsd-bugs/2012-November/… forums.freebsd.org/threads/…
And you can cascade tee like this: cat some.log | tee /dev/tty | tee -a other.log | grep -i 'foo' >> foo.log to 1) get it all to the console, 2) get it all appended to another file, 3) get the foo lines to a different file.
|
84

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.

8 Comments

what if I want to pipe the output of foo to another bar?
@JackTang - I think any further piping on the output of foo will have to be part of the process substitution. Here's an example: echo 'ee' | tee file.txt >(wc -c | tr -d ' ')
This was the solution for me on FreeBSD (no /dev/tty)
@Nick Chammas,To maintain a normal pipeline, you can swap the outputs of tee: echo 'ee' | tee >(cat) | foo | bar.
@Vaelus For me foo sees 2x the ee and none is printed on terminal. Try echo 'ee' | tee >(cat) | grep . vs. echo 'ee' | tee >(cat) | grep x.
|
24

Try:

$ echo 'ee' | tee /dev/stderr | foo 

If using stderr is an option, of course.

Comments

22

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.

Comments

12

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 

2 Comments

oneliner: t=$(tty) echo ee | tee $t | foo | bar
@JackTang That's indeed better but t is useless. You can use echo ee | tee $(tty) | foo but it still has a useless command (tty), given the fact /dev/tty just works.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.