This page explains bash I/O redirection that you can use for redirect stderr and stdout to a file at the shell prompt or in your shell scripts. Bash and other modern Linux/Unix shell provide an I/O redirection facility. There are three default standard files (standard streams) open per process:
- stdin – Get input from keyboard or program. In other words data going into a program.
- stdout – Write information on screen or file.
- stderr – Show error message on screen or file
Tutorial requirements | |
---|---|
Operating system/app | Unix or Linux with bash |
Root privileges required | No |
Difficulty | Easy (rss) |
Estimated completion time | 10m |
Understanding I/O streams numbers
The Unix / Linux standard I/O streams with numbers:
Handle | Name | Description |
0 | stdin | Standard input |
1 | stdout | Standard output |
2 | stderr | Standard error |
Redirecting output
Say you want to save output of the date command to a file. Try:
$ command > output.txt
$ date > today.txt
Use the cat command to view contain of today.txt file, run:
$ cat today.txt
In above example, a file called ‘today.txt’ will be created and it will contain date (what you would see on the screen) if you type the command ‘date’ and execute it. Please note that above command also creates the file today.txty if not present, otherwise overwrites it. If you wish to add/append try the following syntax:
$ command1 >> filename
$ command2 >> filename
$ ls -l >> output.txt
$ date >> output.txt
In short we can redirect stdout to a file simply using the > or >> symbol.
Redirecting the standard error stream to a file
The following will redirect program error message to a file called error.log:
$ program-name 2> error.log
$ command1 2> error.log
For example, use the grep command for recursive search in the $HOME directory and redirect all errors (stderr) to a file name grep-errors.txt as follows:
$ grep -R 'MASTER' $HOME 2> /tmp/grep-errors.txt
$ cat /tmp/grep-errors.txt
Sample outputs:
grep: /home/vivek/.config/google-chrome/SingletonSocket: No such device or address grep: /home/vivek/.config/google-chrome/SingletonCookie: No such file or directory grep: /home/vivek/.config/google-chrome/SingletonLock: No such file or directory grep: /home/vivek/.byobu/.ssh-agent: No such device or address
Redirecting the standard error (stderr) and stdout to file
Use the following syntax:
$ command-name &>file
We can als use the following syntax:
$ command > file-name 2>&1
We can write both stderr and stdout to two different files too. Let us try out our previous grep command example:
$ grep -R 'MASTER' $HOME 2> /tmp/grep-errors.txt 1> /tmp/grep-outputs.txt
$ cat /tmp/grep-outputs.txt
Redirecting stderr to stdout to a file or another command
Here is another useful example where both stderr and stdout sent to the more command instead of a file:
# find /usr/home -name .profile 2>&1 | more
Redirect stderr to stdout
Use the command as follows:
$ command-name 2>&1
$ command-name > file.txt 2>&1
## bash only ##
$ command2 &> filename
$ sudo find / -type f -iname ".env" &> /tmp/search.txt
Redirection takes from left to right. Hence, order matters. For example:
command-name 2>&1 > file.txt ## wrong ##
command-name > file.txt 2>&1 ## correct ##
How to redirect stderr to stdout in Bash script
A sample shell script used to update VM when created in the AWS/Linode server:
#!/usr/bin/env bash # Author - nixCraft under GPL v2.x+ # Debian/Ubuntu Linux script for EC2 automation on first boot # ------------------------------------------------------------ # My log file - Save stdout to $LOGFILE LOGFILE="/root/logs.txt" # My error file - Save stderr to $ERRFILE ERRFILE="/root/errors.txt" # Start it printf "Starting update process ... \n" 1>"${LOGFILE}" # All errors should go to error file apt-get -y update 2>"${ERRFILE}" apt-get -y upgrade 2>>"${ERRFILE}" printf "Rebooting cloudserver ... \n" 1>>"${LOGFILE}" shutdown -r now 2>>"${ERRFILE}"
Our last example uses the exec command and FDs along with trap and custom bash functions:
#!/bin/bash # Send both stdout/stderr to a /root/aws-ec2-debian.log file # Works with Ubuntu Linux too. # Use exec for FD and trap it using the trap # See bash man page for more info # Author: nixCraft under GPL v2.x+ # --------------------------------------------- exec 3>&1 4>&2 trap 'exec 2>&4 1>&3' 0 1 2 3 exec 1>/root/aws-ec2-debian.log 2>&1 # log message log(){ local m="$@" echo "" echo "*** ${m} ***" echo "" } log "$(date) @ $(hostname)" ## Install stuff ## log "Updating up all packages" export DEBIAN_FRONTEND=noninteractive apt-get -y clean apt-get -y update apt-get -y upgrade apt-get -y --purge autoremove ## Update sshd config ## log "Configuring sshd_config" sed -i'.BAK' -e 's/PermitRootLogin yes/PermitRootLogin no/g' -e 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config ## Hide process from other users ## log "Update /proc/fstab to hide process from each other" echo 'proc /proc proc defaults,nosuid,nodev,noexec,relatime,hidepid=2 0 0' >> /etc/fstab ## Install LXD and stuff ## log "Installing LXD/wireguard/vnstat and other packages on this box" apt-get -y install lxd wireguard vnstat expect mariadb-server log "Configuring mysql with mysql_secure_installation" SECURE_MYSQL_EXEC=$(expect -c " set timeout 10 spawn mysql_secure_installation expect \"Enter current password for root (enter for none):\" send \"$MYSQL\r\" expect \"Change the root password?\" send \"n\r\" expect \"Remove anonymous users?\" send \"y\r\" expect \"Disallow root login remotely?\" send \"y\r\" expect \"Remove test database and access to it?\" send \"y\r\" expect \"Reload privilege tables now?\" send \"y\r\" expect eof ") # log to file # echo " $SECURE_MYSQL_EXEC " # We no longer need expect apt-get -y remove expect # Reboot the EC2 VM log "END: Rebooting requested @ $(date) by $(hostname)" reboot
Want both stderr and stdout to the terminal and a log file too?
Try the tee command as follows:
command1 2>&1 | tee filename
Here is how to use it insider shell script too:
#!/usr/bin/env bash { command1 command2 | do_something } 2>&1 | tee /tmp/outputs.log
Conclusion
In this quick tutorial, you learned about three file descriptors, stdin, stdout, and stderr. We can use these Bash descriptors to redirect stdout/stderr to a file or vice versa. See bash man page here:
Operator | Description | Examples |
---|---|---|
command>filename | Redirect stdout to file “filename.” | date > output.txt |
command>>filename | Redirect and append stdout to file “filename.” | ls -l >> dirs.txt |
command 2>filename | Redirect stderr to file “filename.” | du -ch /snaps/ 2> space.txt |
command 2>>filename | Redirect and append stderr to file “filename.” | awk '{ print $4}' input.txt 2>> data.txt |
command &>filename command >filename 2>&1 |
Redirect both stdout and stderr to file “filename.” | grep -R foo /etc/ &>out.txt |
command &>>filename command >>filename 2>&1 |
Redirect both stdout and stderr append to file “filename.” | whois domain &>>log.txt |
🐧 12 comments so far... add one ↓
Category | List of Unix and Linux commands |
---|---|
File Management | cat |
Firewall | Alpine Awall • CentOS 8 • OpenSUSE • RHEL 8 • Ubuntu 16.04 • Ubuntu 18.04 • Ubuntu 20.04 |
Network Utilities | dig • host • ip • nmap |
OpenVPN | CentOS 7 • CentOS 8 • Debian 10 • Debian 8/9 • Ubuntu 18.04 • Ubuntu 20.04 |
Package Manager | apk • apt |
Processes Management | bg • chroot • cron • disown • fg • jobs • killall • kill • pidof • pstree • pwdx • time |
Searching | grep • whereis • which |
User Information | groups • id • lastcomm • last • lid/libuser-lid • logname • members • users • whoami • who • w |
WireGuard VPN | Alpine • CentOS 8 • Debian 10 • Firewall • Ubuntu 20.04 |
What this mean?
$ command > file-name 2>&1
This means redirect stdout to file-name, with that in mind redirect stderr t stdout.
This will lead to both stderr and stdout go to file-name.
Sayed: that line means execute the command while redirecting both stdout and stderr to a file given by file-name.
Greetings!
A slightly more correct is:
The output of the ‘command’ is redirected to a ‘file-name’ and the error chanel (that is the ‘2’ is redirected to a pointer (?) of the output (‘&1’).
So stderr goes to the stdout and that goes to the file.
RudyD
+1
:)
Actually it means “first redirect STDERR to STDOUT, so any errors printed out on STDERR should go to STDOUT. Then, execute ‘command’ and redirect its STDOUT to ‘file-name'” – keeping in mind that at this point STDOUT will also contain whatever is written to STDERR because of the earlier redirection.
Incorrect.
There are two incorrect concepts in your answer.
First is: the redirection happens from left to right. This means that the STDOUT is redirected first.
(When you have > without a stream number, it actually have an implicit 1)
And only after STDERR is redirected to “the same place STDOUT is pointing”, meaning, ‘file-name’
Second wrong concept with your answer is: There are no connection between the descriptors. Changing STDOUT after STDERR had been redirected to STDOUT won’t change STDERR.
It will make STDERR point to STDOUT and then change STDOUT to something else (without touching STDERR)
Here is a more detailed tutorial covering both those misconceptions
http://wiki.bash-hackers.org/howto/redirection_tutorial
I like the &>file one. but not for every stiuation.
In pre-bash4 days you HAD to do it this way:
cat file > file.txt 2>&1
now with bash 4 and greater versions… you can still do it the old way …but …
cat file &> file.txt
The above is bash4+ … some OLD distros may use prebash4 but I think they are alllong gone by now. Just something to keep in mind.
I really love: “command2>&1 | tee logfile.txt”
because tee log’s everything and prints to stdout . So you stil get to see everything! You can even combine sudo to downgrade to a log user account and add date’s subject and store it in a default log directory :)
Hi! good explanation, I’d like to make a function on C that redirects STDIN and SDTOUT to an script, how can I do that, I mean, the exist a library’s on C to put terminal sintaxis on C?, how would you start to do it? I’m very lost with this. Thankyou!
Kewl, thx