bash: redirectionRedirection: stdout, stderr, 2>&1, /dev/null and here-docs
Every process starts with three open streams: stdin (0), stdout (1) and stderr (2). Redirection points them somewhere else before the command runs: > writes a file, >> appends, 2> captures errors, 2>&1 merges errors into output, < reads from a file, and here-documents feed a block of text in.
cmd > file cmd >> file cmd 2> err cmd > file 2>&1 cmd &> file cmd < file cmd <<EOF ... EOF cmd <<< "text"You save a command's output to a file, but the error still lands on your screen. See why, then capture each stream.
./deploy.sh prodThe program and its arguments. Redirections can appear anywhere on the line; bash removes them before running it.
A file descriptor is a small number indexing the process's table of open files. By convention 0 is stdin, 1 stdout, 2 stderr, and a terminal session starts with all three pointing at /dev/pts/N. For an external command bash forks a child, applies the redirections in that child from left to right, then execs the program, which simply writes to fd 1 and 2 without knowing where they lead. > file is open(file, O_WRONLY|O_CREAT|O_TRUNC) followed by dup2(fd, 1); >> uses O_APPEND instead of O_TRUNC; 2>&1 is dup2(1, 2), a copy of where fd 1 points at that moment. That is why > file 2>&1 sends both to the file but 2>&1 > file sends errors to the old stdout. Truncation happens before the command runs, so sort f > f empties f. A pipe | connects one command's fd 1 to the next one's fd 0; stderr skips the pipe unless you use |&. Here-documents are fed to fd 0 through a pipe or temp file.
bash rewires the child's file descriptors, left to right, before the program starts.
bash splits off the redirections > log and 2>&1. They are not arguments: the program will never see them.
> filestdout to file, truncating it first>> filestdout appended to file2> filestderr to file (2>> appends)> file 2>&1stdout and stderr both to file (order matters)&> fileBash shorthand for > file 2>&1 (&>> appends)2>/dev/nullThrow error messages away< filestdin from a filecmd1 |& cmd2Pipe stdout and stderr (same as 2>&1 |)<<EOF ... EOFHere-document: lines as stdin, $vars expanded<<'EOF'Quoted delimiter: no expansion, text is literal<<-EOFStrip leading tabs (not spaces) from the lines<<< "$var"Here-string: one string as stdinset -o noclobber, >|Refuse to overwrite existing files with > / force it onceexec >> log 2>&1Redirect the rest of the script's outputcmd 2>&1 > out.logRedirections apply left to right: stderr copied the terminal before stdout moved, so errors are not in the log.
cmd > out.log 2>&1 # or: cmd &> out.logsort data.txt > data.txtThe shell truncates data.txt before sort reads it, leaving an empty file. Same with sed, grep, awk.
sort -o data.txt data.txt # or write to a temp file, then mvsudo echo 'line' >> /etc/hostsThe redirection is done by your unprivileged shell, not by sudo.
echo 'line' | sudo tee -a /etc/hosts > /dev/nullcmd > /dev/null to silence a noisy commandOnly stdout is discarded; errors still print. Sometimes you want that, often you don't.
cmd > /dev/null 2>&1 # or keep errors: cmd > /dev/nullcat <<EOF > run.sh with $1 and $(date) in the bodyAn unquoted delimiter expands them while writing the file, baking in today's values.
cat <<'EOF' > run.shTry these on a Linux machine of your own. Each task has a hint and a solution.
Run ls on ~/app and on a missing path, sending the normal output to ok.txt and the errors to err.txt. Check both files.
Capture both stdout and stderr of the same ls into one file all.txt, and count its lines.
Create ~/app/motd.txt with three lines using a here-document, where one line shows your user name via $USER.
Append the line '127.0.0.1 lab.local' to /etc/hosts using sudo correctly.
bash: redirectionRedirection: stdout, stderr, 2>&1, /dev/null and here-docs
Every process starts with three open streams: stdin (0), stdout (1) and stderr (2). Redirection points them somewhere else before the command runs: > writes a file, >> appends, 2> captures errors, 2>&1 merges errors into output, < reads from a file, and here-documents feed a block of text in.
cmd > file cmd >> file cmd 2> err cmd > file 2>&1 cmd &> file cmd < file cmd <<EOF ... EOF cmd <<< "text"You save a command's output to a file, but the error still lands on your screen. See why, then capture each stream.
./deploy.sh prodThe program and its arguments. Redirections can appear anywhere on the line; bash removes them before running it.
A file descriptor is a small number indexing the process's table of open files. By convention 0 is stdin, 1 stdout, 2 stderr, and a terminal session starts with all three pointing at /dev/pts/N. For an external command bash forks a child, applies the redirections in that child from left to right, then execs the program, which simply writes to fd 1 and 2 without knowing where they lead. > file is open(file, O_WRONLY|O_CREAT|O_TRUNC) followed by dup2(fd, 1); >> uses O_APPEND instead of O_TRUNC; 2>&1 is dup2(1, 2), a copy of where fd 1 points at that moment. That is why > file 2>&1 sends both to the file but 2>&1 > file sends errors to the old stdout. Truncation happens before the command runs, so sort f > f empties f. A pipe | connects one command's fd 1 to the next one's fd 0; stderr skips the pipe unless you use |&. Here-documents are fed to fd 0 through a pipe or temp file.
bash rewires the child's file descriptors, left to right, before the program starts.
bash splits off the redirections > log and 2>&1. They are not arguments: the program will never see them.
> filestdout to file, truncating it first>> filestdout appended to file2> filestderr to file (2>> appends)> file 2>&1stdout and stderr both to file (order matters)&> fileBash shorthand for > file 2>&1 (&>> appends)2>/dev/nullThrow error messages away< filestdin from a filecmd1 |& cmd2Pipe stdout and stderr (same as 2>&1 |)<<EOF ... EOFHere-document: lines as stdin, $vars expanded<<'EOF'Quoted delimiter: no expansion, text is literal<<-EOFStrip leading tabs (not spaces) from the lines<<< "$var"Here-string: one string as stdinset -o noclobber, >|Refuse to overwrite existing files with > / force it onceexec >> log 2>&1Redirect the rest of the script's outputcmd 2>&1 > out.logRedirections apply left to right: stderr copied the terminal before stdout moved, so errors are not in the log.
cmd > out.log 2>&1 # or: cmd &> out.logsort data.txt > data.txtThe shell truncates data.txt before sort reads it, leaving an empty file. Same with sed, grep, awk.
sort -o data.txt data.txt # or write to a temp file, then mvsudo echo 'line' >> /etc/hostsThe redirection is done by your unprivileged shell, not by sudo.
echo 'line' | sudo tee -a /etc/hosts > /dev/nullcmd > /dev/null to silence a noisy commandOnly stdout is discarded; errors still print. Sometimes you want that, often you don't.
cmd > /dev/null 2>&1 # or keep errors: cmd > /dev/nullcat <<EOF > run.sh with $1 and $(date) in the bodyAn unquoted delimiter expands them while writing the file, baking in today's values.
cat <<'EOF' > run.shTry these on a Linux machine of your own. Each task has a hint and a solution.
Run ls on ~/app and on a missing path, sending the normal output to ok.txt and the errors to err.txt. Check both files.
Capture both stdout and stderr of the same ls into one file all.txt, and count its lines.
Create ~/app/motd.txt with three lines using a here-document, where one line shows your user name via $USER.
Append the line '127.0.0.1 lab.local' to /etc/hosts using sudo correctly.