'How to redirect and append both standard output and standard error to a file with Bash
To redirect standard output to a truncated file in Bash, I know to use:
cmd > file.txt
To redirect standard output in Bash, appending to a file, I know to use:
cmd >> file.txt
To redirect both standard output and standard error to a truncated file, I know to use:
cmd &> file.txt
How do I redirect both standard output and standard error appending to a file? cmd &>> file.txt did not work for me.
Solution 1:[1]
cmd >>file.txt 2>&1
Bash executes the redirects from left to right as follows:
>>file.txt: Openfile.txtin append mode and redirectstdoutthere.2>&1: Redirectstderrto "wherestdoutis currently going". In this case, that is a file opened in append mode. In other words, the&1reuses the file descriptor whichstdoutcurrently uses.
Solution 2:[2]
There are two ways to do this, depending on your Bash version.
The classic and portable (Bash pre-4) way is:
cmd >> outfile 2>&1
A nonportable way, starting with Bash 4 is
cmd &>> outfile
(analog to &> outfile)
For good coding style, you should
- decide if portability is a concern (then use the classic way)
- decide if portability even to Bash pre-4 is a concern (then use the classic way)
- no matter which syntax you use, don't change it within the same script (confusion!)
If your script already starts with #!/bin/sh (no matter if intended or not), then the Bash 4 solution, and in general any Bash-specific code, is not the way to go.
Also remember that Bash 4 &>> is just shorter syntax — it does not introduce any new functionality or anything like that.
The syntax is (beside other redirection syntax) described in the Bash hackers wiki.
Solution 3:[3]
In Bash you can also explicitly specify your redirects to different files:
cmd >log.out 2>log_error.out
Appending would be:
cmd >>log.out 2>>log_error.out
Solution 4:[4]
This should work fine:
your_command 2>&1 | tee -a file.txt
It will store all logs in file.txt as well as dump them in the terminal.
Solution 5:[5]
Solution 6:[6]
Try this:
You_command 1> output.log 2>&1
Your usage of &> x.file does work in Bash 4. Sorry for that: (
Here comes some additional tips.
0, 1, 2, ..., 9 are file descriptors in bash.
0 stands for standard input, 1 stands for standard output, 2 stands for standard error. 3~9 is spare for any other temporary usage.
Any file descriptor can be redirected to other file descriptor or file by using operator > or >>(append).
Usage: <file_descriptor> > <filename | &file_descriptor>
Please see the reference in Chapter 20. I/O Redirection.
Solution 7:[7]
Another approach:
If using older versions of Bash where &>> isn't available, you also can do:
(cmd 2>&1) >> file.txt
This spawns a subshell, so it's less efficient than the traditional approach of cmd >> file.txt 2>&1, and it consequently won't work for commands that need to modify the current shell (e.g. cd, pushd), but this approach feels more natural and understandable to me:
- Redirect standard error to standard output.
- Redirect the new standard output by appending to a file.
Also, the parentheses remove any ambiguity of order, especially if you want to pipe standard output and standard error to another command instead.
To avoid starting a subshell, you instead could use curly braces instead of parentheses to create a group command:
{ cmd 2>&1; } >> file.txt
(Note that a semicolon (or newline) is required to terminate the group command.)
Solution 8:[8]
Redirections from script himself
You could plan redirections from the script itself:
#!/bin/bash
exec 1>>logfile.txt
exec 2>&1
/bin/ls -ld /tmp /tnt
Running this will create/append logfile.txt, containing:
/bin/ls: cannot access '/tnt': No such file or directory
drwxrwxrwt 2 root root 4096 Apr 5 11:20 /tmp
Log to many different files
You could create two different logfiles, appending to one overall log and recreating another last log:
#!/bin/bash
if [ -e last.log ] ;then
mv -f last.log last.old
fi
exec 1> >(tee -a overall.log /dev/tty >last.log)
exec 2>&1
ls -ld /tnt /tmp
Running this script will
- if
last.logalready exist, rename them tolast.old(overwritinglast.oldif they exist). - create a new
last.log. - append everything to
overall.log - output everything to the terminal.
Simple and combined logs
#!/bin/bash
[ -e last.err ] && mv -f last.err lasterr.old
[ -e last.log ] && mv -f last.log lastlog.old
exec 2> >(tee -a overall.err combined.log /dev/tty >last.err)
exec 1> >(tee -a overall.log combined.log /dev/tty >last.log)
ls -ld /tnt /tmp
So you have
last.loglast run log filelast.errlast run error filelastlog.oldprevious run log filelasterr.oldprevious run error fileoverall.logappended overall log fileoverall.errappended overall error filecombined.logappended overall error and log combined file.- still output to the terminal
And for interactive session, use stdbuf:
If you plan to use this in interactive shell, you must tell tee to not buffering his input/output:
# Source this to multi-log your session
[ -e last.err ] && mv -f last.err lasterr.old
[ -e last.log ] && mv -f last.log lastlog.old
exec 2> >(exec stdbuf -i0 -o0 tee -a overall.err combined.log /dev/tty >last.err)
exec 1> >(exec stdbuf -i0 -o0 tee -a overall.log combined.log /dev/tty >last.log)
Once sourced this, you could try:
ls -ld /tnt /tmp
Solution 9:[9]
If you care about the ordering of the content of the two streams, see @ed-morton 's answer to a similar question, here.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
