6

After coming across about 3 tee explanations I regard as undidactic, I would ask for a simple, general, and stepped (if possible) explanation about this command, aimed for Linux newcomers.

I do understand we use it either through a pipe, on a command's stdout, or alternatively, directly a certain file, but I think I miss what the command actually do with this content and when it is useful.

So here's what I ask, hoping to find a didactic explanation, served to newcomers in a clear, stepped way:

  1. How does the command work with either with a command's stdout, or alternatively, the files themselves?

  2. Why is it common to say tee reads standard input? I mean, if I do ls -l, tee doesn't read the syntax ls -l itself but rather the stdout it printed into the session.

Also, if you want, please share a practical example from your daily work as to when tee is very useful to you?

  • 2
    There's no such thing as "stdout of a file". Processes have standard input and standard output. – ilkkachu Apr 29 '17 at 10:33
  • What I meant by that is that when you do cat/less/tail, you get a stdout of the file you choose to display. I really desire to have your opinion on why it isn't semantic and we can't say "this stdout is of file XYZ"? –  Apr 29 '17 at 11:05
  • 1
    “a stdout of the file” is not common terminology, and it is not a good way to describe what happens. To understand this, you must first unlearn what makes you describe it that way. – Gilles 'SO- stop being evil' Apr 29 '17 at 11:43
  • 1
    The "standard output of cat file" will contain the contents of the file file, but it's still the standard output of cat, not of the file. – Kusalananda Apr 29 '17 at 11:45
  • @Benia, Processes communicate through file descriptors, that can point to files, network sockets, pipes to other processes, or whatever. On the userspace-kernel border, fd's are identified by numbers, and by standards and custom, fd's 0,1,2 have a well-understood meaning, namely being the "standard" input, output and error output. As an analogy, imagine the process as a machine, and fd's as tubes coming out of the machine. That works with the pipeline analogy, we have interchangeable machines with tubes labeled "stdin" and "stdout", and a pipeline just connects them together, stdout to stdin. – ilkkachu Apr 29 '17 at 12:00
  • @Benia, what the process does with the stuff flowing in from the tubes, is up to it. cat just copies stuff as-is, wc -l counts the lines in the input and prints that into its output, ls takes no input from stdin, but asks the OS for a file listing, and then prints that to its standard output. – ilkkachu Apr 29 '17 at 12:03
  • 1
    Gilles and kusalananda, I now understand why the term "stdout of a certain file" is uncommon in regards to stdout of a command (that deals with a certain file). Thus I've unlearned that it is best to use this phrase and from now usually say "stdout of a command" (whether piped to be processed with one or more extra commands or not). –  Apr 29 '17 at 12:26

1 Answers1

15

From the tee manual on my system:

The tee utility copies standard input to standard output, making a copy in zero or more files. The output is unbuffered.

So, it reads from standard input and copies it to standard output, and while doing so also duplicates the stream into one or several files.

In the following pipeline, tee would take the output of the first command in the pipeline and copy it to standard output (the terminal) while also making copies of it in the files one, two and three:

$ somecommand | tee one two three

tee has many uses, one is in conjunction with sudo to redirect output to a file owned by root:

$ somecommand | sudo tee /root/somefile >/dev/null

The following would not have worked since the redirection happens as the unprivileged user (it would also run somecommand as root which may be unwanted):

$ sudo somecommand >/root/somefile

An artificial example of appending a fixed set of lines to many files at once (to all users ~/.profile files, assuming that the * expands to the usernames and that the expanded command line does not become too long for the shell to handle):

$ tee -a /home/*/.profile <<'END_NEWPATH'
PATH="$PATH:/opt/bin"
END_NEWPATH

A real example of using tee:

time doas box-build.sh 2>&1 | tee build.out |  grep '^=*>'

This is me building the OpenBSD base system. doas is the OpenBSD "equivalent" of sudo and box-build.sh is a small shell script that does the building (essentially cd /usr/src && make obj && make build). I'd like to store the output of the whole build process, including any errors or warnings, but I don't want to have everything spewing out into my terminal. To this end I use tee to save everything into build.out and then grep to only get a hint of where in the process we are in the terminal.

See also the other questions here that are tagged with the tag.

Kusalananda
  • 333,661
  • Hi Kusalananda. I am sorry to say I didn't understand the first part of the answer hence didn't feel comfortable to keep reading (so I won't make wrong assumptions because of my possibly wrong understanding of the first part) --- You say it reads from standard input and copies it to standard output. What does that mean? If I type ls -l it doesn't work with the ls- l, rather, with data associated with it which I can call "stdout" given after executing ls -l so to me it seems tee works with the stdout, not the stdin which is ls -l. –  Apr 29 '17 at 11:37
  • 1
    @Benia Your difficulty seems to be related to file descriptors and pipes, not to the tee command. See what is meant by connecting STDOUT and STDIN? – Gilles 'SO- stop being evil' Apr 29 '17 at 11:42
  • 1
    @Benia ls -l writes a directory listing to its standard output. If you pipe that stream to tee, it becomes tee's standard input. The standard output of one process is the standard input of the next process in the pipeline. If you say cat file, the standard output of that will contain the contents of the file, but it is the standard output of the cat process. – Kusalananda Apr 29 '17 at 11:42