BASH: How to Redirect Output to File, AND Still Have it on Screen
Linux - SoftwareThis forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
BASH: How to Redirect Output to File, AND Still Have it on Screen
Hi,
Actually this is quite a straightforward question, but somehow i can't find the answer (maybe i'm searching the wrong places). The places i see all teach me how to redirect to file fullstop.
OK, the question...
I know that to redirect the output of a command to a file, i do this:
Code:
ls -l > filename
But what if i want to log the output to file, AND have it displayed on screen (as it would if it were not redirected) as well? Is it possible to do this?
may not work as you expect it.
It is likely that you won't see the stderr and stdout messages in the order in which they were sent to your terminal, simply because stdout (file descriptor 1) is buffered and stderr (2) is not (or was that the other way around - I keep mixing that up). So you'll see the stderr messages first and then the stdout ones (or vice-versa).
If you want the stderr and stdout messages to appear in the right order, you'll need to use a subshell, like so:
(ls -l 2>&1) > file.txt
or
(ls -l 2>&1) | tee file.txt if you want it to appear on screen and in file.txt
This basically says that it should execute the ls -l command, sending stderr to stdout in a subshell. All output from the subshell then comes from the subshell's stdout, and you can capture it in a file or with "tee".
The subshell will only make sure that the stderr and stdout messages appear in the right order.
i just tried it out, using a simple self-written command.sh shell script which has a number of standard and error output.
Seems like
Code:
$ ./command.sh 2>&1 | tee command.log
works fine (even without the sub-shell). Both the terminal and the log file recorded output in the correct order. Of course, adding the parenthesis does no harm.
However,
Code:
$ ./command.sh | tee command.log 2>&1
(with or without parenthesis) displays all the error output followed by all the normal output, and logs only the normal output.
This behaviour seems to run opposite if i use "> command.log" instead of "| tee command.log".
Code:
$ ./command.sh > command.log 2>&1
logs both the normal and error output in the correct order.
Code:
$ (./command.sh > command.log) 2>&1
logs only the normal output and displays only the error output.
Code:
$ ./command.sh 2>&1 > command.log
also logs only the normal output and displays only the error output.
Code:
$ (./command.sh 2>&1) > command.log
logs both normal and error output in the correct order, and displays nothing.
I have to admire you for running all these tests to try to learn. The three pieces I think you're missing in your analysis are "How many commands am I running?", "What order am I redirecting things in?" and "What does | do anyway?"
The first missing piece:
---
For example
Code:
./command.sh >file.txt 2>&1
is only running ONE command, namely "command.sh"
However
Code:
./command.sh 2>&1 | tee file.txt
is running TWO commands, namely "command.sh" and "tee"
When you are running more than one command, you have to remember that EACH command has it's own stdout and stderr. So depending on where you put that little "2>&1" thingy, for example, will determine whether you're redirecting stderr from "command.sh" or from "tee". The same goes for when you're running subshells. The subshell itself is a command so think about whether you're redirecting output from "command.sh" which is running within the subshell, or redirecting output from the subshell itself.
To the second missing piece:
---
Remember "1" = stdout and "2" = stderr. "1" is assumed if you don't provide a number.
Example: ls -l >file.txt is the same as ls -l 1>file.txt
An ampersand can be thought of as meaning: "to the same place as"
Example: ls -l >file.txt 2>&1 means "Send stdout to file.txt and send stderr to the same place as stdout
When we're talking ampersands, "to the same place as" has to also take into account "at this time"
Example
Code:
ls -l >file.txt 2>&1
will send both stdout and stderr to file.txt. Why? Because FIRST stdout was redirected to file.txt and THEN stderr was told to go to the same place as stdout.
On the other hand
Code:
ls -l 2>&1 >file.txt
will send stderr to your screen and stdout to file.txt. Why? Because stderr was redirected to the same place as stdout, but at this time stdout was still pointing to the console. stdout was not redirected to file.txt until AFTER stderr had been redirected. When we are talking AFTER in this example we are talking about placement on the command line. Things specified further to the right are thought of as coming after. So this example does nothing for stderr. stderr is indeed redirected to the same place as stdout, which is the console at the time of the redirection, but stderr was ALREADY pointing there in the first place - resulting in no perceivable change.
To the third missing piece:
---
The pipe symbol "|" can be thought of like a fancy redirection of stdout. It redirects stdout from the first command to the stdin of the second command. So ls -l | tee file.txt says "Take the stdout of ls and send it to the stdin of tee. "tee" is simply a program that takes its stdin and sends it to two places, stdout and also to a file.
The eagle-eyed observer will notice an apparent discrepancy for "|" ...
ls -l 2>&1 | tee file.txt will log BOTH stdout and stderr from ls to file.txt. But at this time, when stderr was redirected to the same place as stdout, stdout had not yet been redirected to tee via the "|". Hmmm ... gotcha! Just one of those things. Never fails - when you try to rationalize your way through something logically ... you run into an exception! This is one of those things were experience tells me "I know it works this way", but when you press me with the "why?" question, I have to hem and haw and say "Because that's the way it is!"
---
Now that you head is spining with this cryptic "explanation", maybe it will start making more sense why you're seeing what you're seeing in your tests.
[edit]Corrected a few of my spelling errors![/edit]
Thanks haertig for the explanation! The results of the tests that i ran certainly makes sense now.
As for
Code:
ls -l 2>&1 | tee file.txt
Can we say that:
First, the stderr is redirected to where the stdout goes.
Then we send whatever "comes out" of where stdout normally "comes out", and send it to tee.
And since at that point, both stdout and stderr "comes out" of that place, both are piped to tee, which both displays and sends it to file.
?
Can we say that:
First, the stderr is redirected to where the stdout goes.
Then we send whatever "comes out" of where stdout normally "comes out", and send it to tee.
And since at that point, both stdout and stderr "comes out" of that place, both are piped to tee, which both displays and sends it to file.
Distribution: O.A.M. (Overmonitoring Address Matrix) Release 2.2 with 2120 Patch
Posts: 37
Rep:
ampersand
Actually, I don't think that the ampersand strictly means "to the same place as". I think that the ampersand is necessary so that "1" isn't interpreted as a file.
The code
Actually, I don't think that the ampersand strictly means "to the same place as". I think that the ampersand is necessary so that "1" isn't interpreted as a file.
The code
Code:
ls 1>1
will redirect stdout to a file named "1"
Whereas
Code:
ls 1>&1
will redirect stdout to stdout. :)
Good point, see REDIRECTION in bash man. As you can see, the manual is far from clear on this:
Quote:
The general format for redirecting output is:
[n]>word
[...]
>&word
[...] is semantically equiva‐
lent to
>word 2>&1
[...]
The operator
[n]>&word
is used similarly to duplicate output file descriptors. (That is: "If word expands to one or
more digits, the file descriptor denoted by n is made to be a copy of
that file descriptor.", doru's note) If n is not
specified, the standard output (file descriptor 1) is used. If the
digits in word do not specify a file descriptor open for output, a re‐
direction error occurs. As a special case, if n is omitted, and word
does not expand to one or more digits, the standard output and standard
error are redirected as described previously.
Last edited by doru; 11-22-2010 at 12:12 PM.
Reason: completion
The eagle-eyed observer will notice an apparent discrepancy for "|" ...
ls -l 2>&1 | tee file.txt will log BOTH stdout and stderr from ls to file.txt. But at this time, when stderr was redirected to the same place as stdout, stdout had not yet been redirected to tee via the "|". Hmmm ... gotcha! Just one of those things. Never fails - when you try to rationalize your way through something logically ... you run into an exception! This is one of those things were experience tells me "I know it works this way", but when you press me with the "why?" question, I have to hem and haw and say "Because that's the way it is!"
It makes sense in the light of the shell's operation. The "|" is actioned in "3. Parses the tokens into simple and compound commands" and the ">"s are actioned in "5. Performs any necessary redirections ..." so, in this case, the ls command's stdout is already configured for redirection to the tee command by the time the ">"s are processed.
It makes sense in the light of the shell's operation. The "|" is actioned in "3. Parses the tokens into simple and compound commands" and the ">"s are actioned in "5. Performs any necessary redirections ..." so, in this case, the ls command's stdout is already configured for redirection to the tee command by the time the ">"s are processed.
Thank you. My Ubuntu 10.04 bash man does not really say that.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.