I/O Redirection and Pipes
File Descriptors
Section titled “File Descriptors”Every Linux process starts with three standard file descriptors, and can open additional ones as Needed. File descriptors are non-negative integers maintained by the kernel per-process.
Standard File Descriptors
Section titled “Standard File Descriptors”| FD | Name | Default Destination | Description |
|---|---|---|---|
| 0 | stdin | Keyboard (terminal) | Input stream |
| 1 | stdout | Terminal | Normal output |
| 2 | stderr | Terminal | Error and diagnostic output |
| 3+ | custom | (none) | Application-defined file descriptors |
# View file descriptors for a processls -la /proc/$$/fd/# 0 -> /dev/pts/0# 1 -> /dev/pts/0# 2 -> /dev/pts/0# 255 -> /dev/pts/0 (script file)
# View limitscat /proc/$$/limits | grep "open files"Redirection Operators
Section titled “Redirection Operators”Output Redirection
Section titled “Output Redirection”# Truncate and write stdoutcommand > file.txtcommand 1> file.txt
# Append stdoutcommand >> file.txtcommand 1>> file.txt
# Redirect stderrcommand 2> error.log
# Append stderrcommand 2>> error.log
# Redirect both stdout and stderr (POSIX)command > output.log 2>&1
# Redirect both (bash shorthand)command &> output.logcommand &>> output.log
# Discard outputcommand > /dev/nullcommand 2> /dev/nullcommand &> /dev/nullInput Redirection
Section titled “Input Redirection”# Read stdin from filecommand < input.txtcommand 0< input.txt
# Read from here-documentcommand << EOFline 1line 2$VARIABLE expanded hereEOF
# Read from here-document with tab strippingcommand <<- EOF tab-indented content (tabs stripped, spaces preserved)EOF
# Read from here-stringcommand <<< "single line of input"grep "error" <<< "this line has an error"
# Open file for reading and writing (<>)exec 3<> file.txtread -r line <&3echo "new line" >&3exec 3<&-Redirection Order Matters
Section titled “Redirection Order Matters”# WRONG: stderr redirect sees the original stdout (terminal)command 2>&1 > file.txt# stderr goes to terminal, stdout goes to file.txt
# CORRECT: stdout redirect happens first, then stderr copies itcommand > file.txt 2>&1# Both stdout and stderr go to file.txtflowchart LR
A[command] -->|stdout| B[file.txt]
A -->|stderr| C[stdout copy]
C --> BRedirection in Different Contexts
Section titled “Redirection in Different Contexts”# Redirect specific command in a pipelinels -la /nonexistent 2>&1 | grep -i "no such"
# Redirect a block of code{ echo "start" ls -la /nonexistent echo "end"} > output.log 2>&1
# Redirect a functionmyfunc() { echo "stdout from function" echo "stderr from function" >&2}myfunc > func_out.log 2> func_err.log
# Redirect a loopfor file in *.log; do wc -l "$file"done > line_counts.txtAnonymous Pipes
Section titled “Anonymous Pipes”# Basic pipe — stdout of left goes to stdin of rightcommand1 | command2
# Pipeline return code is the exit status of the last commandfalse | trueecho $? # 0 (true"s exit code)
# With pipefail, pipeline fails if ANY command failsset -o pipefailfalse | trueecho $? # 1 (false's exit code)Pipe Buffer
Section titled “Pipe Buffer”# View pipe buffer sizecat /proc/sys/fs/pipe-max-size# 1048576 (1 MiB on modern Linux)
# The default buffer is 64 KiB (65536 bytes) since kernel 2.6.11# When the buffer is full, the writer blocks
# Increase pipe buffer size (requires CAP_SYS_RESOURCE)# This can improve throughput in high-volume pipelinesdd if=/dev/urandom bs=1M count=100 | md5sumPIPE_BUF and Atomic Writes
Section titled “PIPE_BUF and Atomic Writes”PIPE_BUF (typically 4096 bytes on Linux):- Writes up to PIPE_BUF bytes to a pipe are atomic- Writes larger than PIPE_BUF may be interleaved with writes from other processes- Multiple writers to the same pipe: writes <= PIPE_BUF are guaranteed atomic- Single writer: all writes are effectively atomic (no interleaving)
View PIPE_BUF: getconf PIPE_BUF / # 4096SIGPIPE
Section titled “SIGPIPE”When a process writes to a pipe whose reader has closed, the kernel sends SIGPIPE to the writer. The default action for SIGPIPE is to terminate the process.
# Demonstrate SIGPIPEyes | head -n 5# "yes" writes infinitely; "head" reads 5 lines and closes stdin# "yes" receives SIGPIPE and terminates
# Ignore SIGPIPE (useful in network programming)trap '' PIPE# Now write to a closed pipe returns EPIPE instead of killing the process
# Check for broken pipe in scriptsyes | head -n 5; echo "exit: $?"Named Pipes (FIFOs)
Section titled “Named Pipes (FIFOs)”Named pipes appear as files in the filesystem but behave like anonymous pipes. They allow unrelated Processes to communicate.
Creating and Using Named Pipes
Section titled “Creating and Using Named Pipes”# Create a named pipemkfifo /tmp/my_pipe
# In terminal 1: write to the pipe (blocks until reader connects)echo "hello from writer" > /tmp/my_pipe
# In terminal 2: read from the pipecat < /tmp/my_pipe
# The pipe is unidirectional by default# For bidirectional communication, use two pipesmkfifo /tmp/pipe_in /tmp/pipe_outNamed Pipe Use Cases
Section titled “Named Pipe Use Cases”# 1. Simple IPC between processesmkfifo /tmp/cmd_pipe# Writer:while true; do read -r cmd echo "Processing: $cmd"done < /tmp/cmd_pipe# Reader (in another terminal):echo "status" > /tmp/cmd_pipeecho "restart" > /tmp/cmd_pipe
# 2. Sequential processingmkfifo /tmp/buffersort data.txt > /tmp/buffer &uniq < /tmp/buffer > /tmp/buffer2 &awk '{print $2}' < /tmp/buffer2
# 3. Log multiplexingmkfifo /tmp/log_pipetail -f /tmp/log_pipe | while read -r line; do echo "$(date) $line" >> /var/log/app.log echo "$line" | grep -i "error" >> /var/log/errors.logdone &
# Send logs to the pipeapp1 --log /tmp/log_pipe &app2 --log /tmp/log_pipe &
# 4. Progress monitoringmkfifo /tmp/progress# Long-running process writes progressfor i in $(seq 1 100); do echo "$i" sleep 0.1done > /tmp/progress# Monitor reads progresstail -f /tmp/progressNamed Pipe Properties
Section titled “Named Pipe Properties”# Check pipe statusls -la /tmp/my_pipe# prw-r--r-- 1 user user 0 ...
# Named pipes have zero sizedu /tmp/my_pipe # 0
# Named pipes persist until deletedrm /tmp/my_pipe
# Multiple readers: only one gets each message# Multiple writers: messages may interleaveProcess Substitution
Section titled “Process Substitution”Process substitution creates a temporary file descriptor (using /dev/fd/ or a named pipe) that Connects to the input or output of a process.
Input Process Substitution
Section titled “Input Process Substitution”# Compare output of two commandsdiff <(sort file1.txt) <(sort file2.txt)
# Pass command output to a command expecting a file argumentwc -l <(find /etc -name "*.conf")md5sum <(tar cf - /home/user/docs)
# Multiple inputspaste <(cut -f1 data.txt) <(cut -f2 data.txt)
# Feed grep patterns from command outputgrep -f <(echo -e "error\nwarning\ncritical") /var/log/syslogOutput Process Substitution
Section titled “Output Process Substitution”# Split stdout and stderr to different processescommand > >(grep "INFO" >> info.log) 2> >(grep "ERROR" >> error.log)
# Parallel processingcat largefile.txt | tee >(process1 &>/dev/null) | process2
# Background loggingmy_long_command > >(while read -r line; do echo "$(date) $line"done >> /var/log/mycommand.log) 2>&1tee reads from stdin and writes to stdout and one or more files simultaneously.
# Write to stdout and a filecommand | tee output.log
# Append modecommand | tee -a output.log
# Write to multiple filescommand | tee file1.log file2.log file3.log
# Discard stdout, write only to filescommand | tee output.log > /dev/null
# With sudo (write to root-owned files)command | sudo tee /etc/config.conf
# tee with pipescommand | tee /dev/tty | grep error
# Interactive monitoringdmesg | tee /tmp/dmesg.logtee Use Cases
Section titled “tee Use Cases”# 1. Log and display simultaneously./deploy.sh 2>&1 | tee /var/log/deploy-$(date +%Y%m%d-%H%M%S).log
# 2. Audit trailsudo iptables-save | tee /etc/iptables/rules.v4 | iptables-restore
# 3. Multi-stream loggingtail -f /var/log/syslog | tee >(grep error >> errors.log) >(grep warn >> warnings.log) > /dev/null
# 4. Checkpoint processingcat large_input | tee /tmp/checkpoint | process_data > outputxargs reads items from stdin and executes a command with them as arguments. It handles argument List limits and provides parallel execution.
Basic Usage
Section titled “Basic Usage”# Build arguments from stdinecho "file1.txt file2.txt file3.txt" | xargs rmfind /tmp -name "*.tmp" -print0 | xargs -0 rm
# Limit arguments per invocationecho {1..100} | tr ' ' '\n' | xargs -n 5 echo
# Interactive mode (confirm each operation)find . -name "*.log" | xargs -p rmParallel Execution
Section titled “Parallel Execution”# Run 4 processes in parallelfind . -name "*.jpg" | xargs -P 4 -I {} convert {} -resize 50% {}_small.jpg
# Parallel downloadscat urls.txt | xargs -P 8 -I {} wget -q {}
# Parallel compressionfind . -name "*.log" -print0 | xargs -0 -P 4 -I {} gzip {}
# Run with progresscat files.txt | xargs -P 4 -I {} sh -c 'echo "Processing {}"; process "{}"'xargs Options
Section titled “xargs Options”# -I {} — replace stringecho "a b c" | xargs -I {} echo "item: {}"# item: a b c
# -n N — max arguments per commandseq 1 10 | xargs -n 3 echo# 1 2 3# 4 5 6# 7 8 9# 10
# -0 — null-delimited input (safe for filenames with spaces/newlines)find . -print0 | xargs -0 -n 1 echo
# -d DELIM — custom delimiterecho "a:b:c" | xargs -d ': " -I {} echo "item: {}"
# --max-procs or -P — parallel executionseq 1 20 | xargs -P 4 -I {} sleep 1 && echo {}
# -L N — max lines per commandcat addresses.txt | xargs -L 1 curl -s -o /dev/null -w "%{http_code}\n"xargs vs for Loop
Section titled “xargs vs for Loop”# xargs — faster, handles argument limitsfind . -name "*.log" | xargs gzip
# for loop — safer, easier to add logicfind . -name "*.log" -print0 | while IFS= read -r -d ''" file; do echo "Compressing $file" gzip "$file"done