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