Advanced
File Descriptors - Advanced
Custom File Descriptors (3-255)
Beyond 0, 1, and 2, you can use file descriptors 3-255 for custom purposes:
# Open FD 3 for reading
exec 3< input.txt
# Open FD 4 for writing
exec 4> output.txt
# Use them
cat <&3 # Read from FD 3
echo "Hello" >&4 # Write to FD 4
# Close them
exec 3<&- # Close FD 3
exec 4>&- # Close FD 4
Advanced Stream Manipulation
Swapping stdout and stderr
# Swap stdout and stderr
command 3>&2 2>&1 1>&3 3>&-
# Explanation:
# 3>&2 - FD 3 copies stderr
# 2>&1 - stderr copies stdout
# 1>&3 - stdout copies FD 3 (original stderr)
# 3>&- - Close FD 3
Selective Filtering
# Filter only stderr through a command
{ command 2>&1 1>&3 | grep "ERROR" >&2; } 3>&1
# Send stdout and stderr to different commands
{ { command | stdout_processor; } 2>&1 1>&3 | stderr_processor; } 3>&1
Process Substitution with FDs
# Read from multiple sources simultaneously
while IFS= read -r line1 <&3 && IFS= read -r line2 <&4; do
echo "File1: $line1"
echo "File2: $line2"
done 3< file1.txt 4< file2.txt
# Dynamic FD allocation
exec {fd}< input.txt # Bash allocates next available FD
echo "Allocated FD: $fd"
cat <&$fd
exec {fd}<&- # Close it
Named Pipes (FIFOs) with FDs
# Create a named pipe
mkfifo mypipe
# Background writer
exec 3> mypipe
for i in {1..10}; do
echo "Message $i" >&3
sleep 1
done &
# Reader
while IFS= read -r line; do
echo "Received: $line"
done < mypipe
# Cleanup
exec 3>&-
rm mypipe
Complex Example: Parallel Processing
#!/bin/bash
# parallel-processor.sh - Process multiple files in parallel
MAX_JOBS=4
JOB_PIPE=$(mktemp -u)
mkfifo "$JOB_PIPE"
exec 3<>"$JOB_PIPE"
rm "$JOB_PIPE"
# Initialize job slots
for ((i=0; i<MAX_JOBS; i++)); do
echo >&3
done
# Process files
for file in *.txt; do
# Wait for available slot
read -u 3
# Start background job
{
echo "Processing $file..."
# Do work here
sleep $((RANDOM % 5))
echo "Done with $file"
# Release slot
echo >&3
} &
done
# Wait for all jobs
wait
exec 3>&-
Debugging with FDs
# debug-trace.sh - Advanced debugging with custom FD
DEBUG_FD=9
DEBUG_FILE="/tmp/debug-$$.log"
# Enable debug mode
enable_debug() {
exec 9>> "$DEBUG_FILE"
export PS4='+ [${BASH_SOURCE}:${LINENO}] '
set -x
}
# Debug output goes to FD 9
debug() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&9
}
# Usage
enable_debug
debug "Script starting"
# Your code here
Backlinks