Keywords: tee command | standard output | standard error | process substitution | file redirection | Bash scripting
Abstract: This article provides an in-depth exploration of using the tee command to handle both standard output and standard error in Linux/bash environments. Through analysis of process substitution and file redirection mechanisms, it explains how to redirect stdout and stderr to separate files while maintaining terminal display. The article compares different implementation approaches between Bash and POSIX shell, with detailed code examples and explanations.
Requirements for Redirecting Standard Output and Standard Error
In Linux and Unix systems, standard output (stdout) and standard error (stderr) are two independent output streams. When we need to save the contents of both streams to files while maintaining terminal display, the tee command provides an ideal solution. While basic tee usage only handles standard output, through clever combination we can handle both output streams simultaneously.
Process Substitution Solution in Bash
In Bash shell, process substitution offers the most elegant solution. The following command can redirect standard output to stdout.log and standard error to stderr.log simultaneously, while maintaining display of both streams in the terminal:
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)Let's analyze each part of this command in detail:
The > >(tee -a stdout.log) portion uses process substitution to create a FIFO (named pipe) and attaches the tee command to this pipe. Standard output is redirected to this pipe, and the tee command reads from the pipe, writing to both the stdout.log file and displaying on standard output.
The 2> >(tee -a stderr.log >&2) portion uses the same mechanism for standard error. The key here is the >&2 redirection, which redirects tee's standard output back to standard error, ensuring error messages are correctly displayed in the terminal.
POSIX Shell Compatible Implementation
For POSIX shells that don't support process substitution, we need to manually create and manage FIFOs:
out="${TMPDIR:-/tmp}/out.$$"
err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"This implementation is more complex but offers better compatibility. We first create two temporary FIFO files, use the trap command to ensure cleanup upon script exit, then start two tee processes listening to these FIFOs, and finally redirect the command's output to the corresponding FIFOs.
Limitations of Simplified Approaches
While simple redirection approaches like ./aaa.sh 2>&1 | tee -a log or Bash 4+'s ./aaa.sh |& tee -a log can merge standard error into standard output, this approach has significant limitations: it cannot distinguish between standard output and standard error content, as both are mixed in the same stream. This is insufficient for scenarios requiring separate analysis of normal output and error information.
Practical Application Scenarios
This technique is highly useful in various automation scripts and system administration tasks. For example, in continuous integration environments, we need to record both normal output and error information from build processes; in system monitoring scripts, separate logging of normal operations and errors is required; during development debugging, simultaneous viewing of terminal output and detailed log file saving is essential.
In-depth Technical Analysis
The core advantage of process substitution lies in its creation of temporary FIFO pipes that operate in memory, avoiding disk I/O overhead. Each tee process runs independently without interference, ensuring the integrity and real-time nature of output streams. The file descriptor redirection chain ensures correct data flow: from command output to FIFO, to tee process, and finally to both file writing and terminal display.
In terms of performance, while this solution creates additional processes, the efficiency of FIFOs minimizes overall performance impact. More importantly, it provides complete output separation and real-time display functionality, which is crucial in debugging and monitoring scenarios.