Coprocesses
Chapter 41: Coprocesses in Bash
Section titled “Chapter 41: Coprocesses in Bash”Overview
Section titled “Overview”Coprocesses are a powerful feature in Bash that allow you to create background processes with two-way communication. They enable you to send input to and receive output from background processes, making them ideal for interactive scripts and scenarios requiring continuous communication between the shell and a process.
What is a Coprocess?
Section titled “What is a Coprocess?”A coprocess is a background process that has its stdin and stdout connected to the parent shell through named pipes. Unlike regular background jobs, coprocesses allow bidirectional communication.
┌────────────────────────────────────────────────────────────────┐│ Coprocess Architecture │├────────────────────────────────────────────────────────────────┤│ ││ Shell ││ ───── ││ ││ ┌─────────────────────┐ ││ │ Coprocess │ ││ │ (bg process) │──── stdout ───► ││ │ │◄─── stdin ──── ││ └─────────────────────┘ ││ ││ write -p "data" ───► coprocess input ││ read -p variable ◄─── coprocess output ││ ││ Key difference from regular background jobs: ││ - Regular bg job: stdout only, one-way ││ - Coprocess: stdin AND stdout, two-way ││ │└────────────────────────────────────────────────────────────────┘Basic Syntax
Section titled “Basic Syntax”Starting a Coprocess
Section titled “Starting a Coprocess”# Basic coprocess syntaxcoproc NAME { command; }
# Example with bc calculatorcoproc BC { bc -l; }
# Using named pipescoproc myprocess { while read line; do echo "Processed: $line"; done; }Accessing File Descriptors
Section titled “Accessing File Descriptors”When you start a coprocess, Bash creates these variables:
# ${NAME[0]} - PID of the coprocess# ${NAME[1]} - File descriptor for writing to coprocess stdin# ${NAME[2]} - File descriptor for reading from coprocess stdout
# Example:coproc BC { bc -l; }
# BC_PID (or ${BC[0]}) contains the PIDecho "BC PID: $BC_PID"
# Write to stdin using ${BC[1]}echo "scale=2" >&${BC[1]}
# Read from stdout using ${BC[0]}read -r result <&${BC[0]}echo "Result: $result"Practical Examples
Section titled “Practical Examples”Interactive Calculator
Section titled “Interactive Calculator”#!/usr/bin/env bash# Calculator using coprocess - DevOps use case for calculations
set -euo pipefail
# Start bc as coprocess for precision calculationscoproc BC { bc -l; }
# Set precisionecho "scale=4" >&${BC[1]}
# Perform various calculationsecho "10 + 5" >&${BC[1]}read -r sum <&${BC[0]}echo "10 + 5 = $sum"
echo "100 / 3" >&${BC[1]}read -r result <&${BC[0]}echo "100 / 3 = $result"
echo "2^10" >&${BC[1]}read -r power <&${BC[0]}echo "2^10 = $power"
# Clean quitecho "quit" >&${BC[1]}
# Kill the coprocesskill ${BC_PID} 2>/dev/null || trueecho "Calculator closed"Continuous Data Processing
Section titled “Continuous Data Processing”#!/usr/bin/env bash# Process log entries through awk coprocess in real-time
set -euo pipefail
# Start awk coprocess for log processingcoproc AWK { awk '{ # Count by log level if (/ERROR/) errors++ if (/WARN/) warns++ if (/INFO/) infos++ END { print "ERRORS:", errors print "WARNINGS:", warns print "INFO:", infos } }';}
# Send log data to processcat /var/log/syslog | while read -r line; do echo "$line" >&${AWK[1]}done
# Close input to trigger END blockexec {AWK[1]}>&-
# Read final resultswhile read -r line <&${AWK[0]}; do echo "Results: $line"doneDatabase Connection Pool
Section titled “Database Connection Pool”#!/usr/bin/env bash# Maintain persistent database connection for multiple queries
set -euo pipefail
# Start PostgreSQL interactive session as coprocesscoproc PSQL { psql -h localhost -U appuser -d myapp}
# Wait for connection to establishsleep 2
# Execute multiple queries through same connectionecho "SELECT version();" >&${PSQL[1]}read -r version <&${PSQL[0]}echo "$version"
echo "SELECT current_database();" >&${PSQL[1]}read -r dbname <&${PSQL[0]}echo "$dbname"
echo "SELECT count(*) FROM users;" >&${PSQL[1]}read -r count <&${PSQL[0]}echo "$count"
# Quit gracefullyecho "\q" >&${PSQL[1]}Advanced Usage
Section titled “Advanced Usage”Multiple Coprocesses
Section titled “Multiple Coprocesses”#!/usr/bin/env bash# Use multiple coprocesses simultaneously
set -euo pipefail
# Start two coprocesses for parallel processingcoproc PROCESS1 { sed 's/.*/P1: &/'; }coproc PROCESS2 { sed 's/.*/P2: &/'; }
# Send data to first processecho "data1" >&${PROCESS1[1]}echo "data2" >&${PROCESS2[1]}
# Read results from bothread -r out1 <&${PROCESS1[0]}read -r out2 <&${PROCESS2[0]}
echo "Process 1: $out1"echo "Process 2: $out2"
# Cleanupkill ${PROCESS1_PID} ${PROCESS2_PID} 2>/dev/null || trueReal-time Server Communication
Section titled “Real-time Server Communication”#!/usr/bin/env bash# Communicate with network service in real-time
set -euo pipefail
# Connect to server (using nc for demonstration)coproc SOCAT { nc localhost 8080; }
# Send HTTP requestecho -e "GET /health HTTP/1.1\r\nHost: localhost\r\n\r" >&${SOCAT[1]}
# Read responsewhile read -r line <&${SOCAT[0]}; do echo "$line"done
# Close connectionexec {SOCAT[1]}>&-Error Handling
Section titled “Error Handling”Proper Cleanup
Section titled “Proper Cleanup”#!/usr/bin/env bash# Coprocess with proper error handling and cleanup
set -euo pipefail
cleanup() { # Kill coprocess if running if [[ -n "${BC_PID:-}" ]] && kill -0 "$BC_PID" 2>/dev/null; then echo "quit" >&${BC[1]} 2>/dev/null || true kill "$BC_PID" 2>/dev/null || true fi}
trap cleanup EXIT
# Start coprocesscoproc BC { bc -l; }
# Use coprocessecho "scale=2; 22/7" >&${BC[1]}read -r result <&${BC[0]}echo "Result: $result"Timeout Handling
Section titled “Timeout Handling”#!/usr/bin/env bash# Coprocess with timeout
set -euo pipefail
coproc BC { bc -l; }
# Set timeouttimeout=5
echo "100/3" >&${BC[1]}
# Read with timeoutif read -t "$timeout" -r result <&${BC[0]}; then echo "Result: $result"else echo "Timeout waiting for response" kill ${BC_PID} 2>/dev/null || truefiDevOps Use Cases
Section titled “DevOps Use Cases”CI/CD Pipeline Integration
Section titled “CI/CD Pipeline Integration”#!/usr/bin/env bash# Use coprocess for continuous integration build process
set -euo pipefail
# Start build processcoproc BUILD { make build 2>&1}
# Monitor build progresswhile read -r line <&${BUILD[0]}; do echo "[BUILD] $line"
# Check for errors if echo "$line" | grep -q "ERROR"; then echo "Build failed!" kill ${BUILD_PID} 2>/dev/null || true exit 1 fi
# Check for success if echo "$line" | grep -q "Build complete"; then echo "Build successful!" fidoneMetrics Collection
Section titled “Metrics Collection”#!/usr/bin/env bash# Collect system metrics continuously
set -euo pipefail
# Start metrics collectorcoproc METRICS { while true; do echo "cpu:$(top -bn1 | grep "Cpu(s)" | awk '{print $2}')" echo "mem:$(free | grep Mem | awk '{print ($3/$2) * 100}')" sleep 5 done}
# Collect for 30 secondsend=$((SECONDS + 30))while [[ $SECONDS -lt $end ]]; do if read -t 2 -r metric <&${METRICS[0]}; then echo "[METRIC] $metric" fidone
kill ${METRICS_PID} 2>/dev/null || trueSummary
Section titled “Summary”In this chapter, you learned:
- ✅ What coprocesses are and how they work
- ✅ Basic syntax and file descriptor access
- ✅ Two-way communication with coprocesses
- ✅ Practical examples for DevOps use cases
- ✅ Multiple coprocess handling
- ✅ Error handling and cleanup
- ✅ Real-world DevOps applications
Next Steps
Section titled “Next Steps”Continue to the next chapter to learn about Process Substitution.
Previous Chapter: Container Automation Next Chapter: Process Substitution