1#!/usr/bin/env bash
2set -euo pipefail
3
4# Process Substitution Examples
5# Demonstrates <(...) and >(...) for advanced I/O patterns
6
7echo "=== Process Substitution Demonstrations ==="
8echo
9
10# --- Demo 1: Comparing output of two commands ---
11demo_diff_commands() {
12 echo "1. Comparing Output of Two Commands"
13 echo "------------------------------------"
14
15 # Create two sample files
16 cat > /tmp/file1.txt << EOF
17apple
18banana
19cherry
20date
21elderberry
22EOF
23
24 cat > /tmp/file2.txt << EOF
25apple
26blueberry
27cherry
28date
29fig
30EOF
31
32 echo "Comparing two files with diff:"
33 echo
34
35 # Traditional approach (requires temp files)
36 echo "File 1 contents:"
37 cat /tmp/file1.txt
38 echo
39 echo "File 2 contents:"
40 cat /tmp/file2.txt
41 echo
42
43 # Using process substitution - compare sorted versions
44 echo "Diff output (process substitution):"
45 if diff <(sort /tmp/file1.txt) <(sort /tmp/file2.txt); then
46 echo "Files are identical"
47 else
48 echo "(showing differences above)"
49 fi
50
51 echo
52
53 # Another example: compare directory listings
54 echo "Comparing directory contents:"
55 mkdir -p /tmp/dir1 /tmp/dir2
56 touch /tmp/dir1/{a,b,c}.txt
57 touch /tmp/dir2/{b,c,d}.txt
58
59 echo "Files only in dir1:"
60 comm -23 <(ls /tmp/dir1) <(ls /tmp/dir2)
61
62 echo "Files only in dir2:"
63 comm -13 <(ls /tmp/dir1) <(ls /tmp/dir2)
64
65 echo "Files in both:"
66 comm -12 <(ls /tmp/dir1) <(ls /tmp/dir2)
67
68 # Cleanup
69 rm -rf /tmp/file1.txt /tmp/file2.txt /tmp/dir1 /tmp/dir2
70
71 echo
72}
73
74# --- Demo 2: Multiple inputs to a command ---
75demo_multiple_inputs() {
76 echo "2. Feeding Multiple Inputs to a Command"
77 echo "----------------------------------------"
78
79 # Use paste to combine output from multiple commands
80 echo "Combining outputs side-by-side with paste:"
81 paste <(echo -e "1\n2\n3\n4\n5") \
82 <(echo -e "one\ntwo\nthree\nfour\nfive") \
83 <(echo -e "I\nII\nIII\nIV\nV")
84
85 echo
86
87 # Use join to merge related data
88 echo "Joining related data:"
89
90 # Process 1: user IDs and names
91 # Process 2: user IDs and emails
92 join <(echo -e "1 Alice\n2 Bob\n3 Charlie") \
93 <(echo -e "1 alice@example.com\n2 bob@example.com\n3 charlie@example.com")
94
95 echo
96}
97
98# --- Demo 3: Avoiding subshell variable issues ---
99demo_avoid_subshell() {
100 echo "3. Avoiding Subshell Variable Issues"
101 echo "-------------------------------------"
102
103 # Problem: Variables set in pipeline are lost (subshell)
104 echo "Problem - variable in pipeline (lost):"
105 count=0
106 echo -e "a\nb\nc" | while read -r line; do
107 ((count++))
108 done
109 echo " Count after pipeline: $count (still 0!)"
110
111 echo
112
113 # Solution: Use process substitution (no subshell)
114 echo "Solution - process substitution (preserved):"
115 count=0
116 while read -r line; do
117 ((count++))
118 done < <(echo -e "a\nb\nc")
119 echo " Count after process sub: $count (correct!)"
120
121 echo
122}
123
124# --- Demo 4: Reading multiple streams simultaneously ---
125demo_multiple_streams() {
126 echo "4. Reading Multiple Streams Simultaneously"
127 echo "------------------------------------------"
128
129 # Read from two sources in parallel
130 echo "Reading two files line-by-line in parallel:"
131
132 # Create sample files
133 seq 1 5 > /tmp/numbers.txt
134 echo -e "one\ntwo\nthree\nfour\nfive" > /tmp/words.txt
135
136 while IFS= read -r num <&3 && IFS= read -r word <&4; do
137 echo " $num: $word"
138 done 3< /tmp/numbers.txt 4< /tmp/words.txt
139
140 # Cleanup
141 rm -f /tmp/numbers.txt /tmp/words.txt
142
143 echo
144}
145
146# --- Demo 5: Process substitution for writing ---
147demo_write_process_sub() {
148 echo "5. Process Substitution for Writing >(cmd)"
149 echo "-------------------------------------------"
150
151 echo "Splitting output to multiple destinations:"
152
153 # Generate data and send to multiple processors
154 {
155 echo "apple"
156 echo "banana"
157 echo "cherry"
158 echo "date"
159 } | tee >(grep 'a' > /tmp/has_a.txt) \
160 >(grep 'e' > /tmp/has_e.txt) \
161 >(wc -l > /tmp/count.txt) \
162 > /dev/null
163
164 # Small delay to ensure all processes complete
165 sleep 0.1
166
167 echo "Words containing 'a':"
168 cat /tmp/has_a.txt
169
170 echo
171 echo "Words containing 'e':"
172 cat /tmp/has_e.txt
173
174 echo
175 echo "Total word count:"
176 cat /tmp/count.txt
177
178 # Cleanup
179 rm -f /tmp/has_a.txt /tmp/has_e.txt /tmp/count.txt
180
181 echo
182}
183
184# --- Demo 6: Complex data processing pipeline ---
185demo_complex_pipeline() {
186 echo "6. Complex Data Processing Pipeline"
187 echo "------------------------------------"
188
189 # Simulate log file
190 cat > /tmp/access.log << EOF
191192.168.1.1 - - [01/Jan/2024:10:00:00] "GET /index.html HTTP/1.1" 200 1234
192192.168.1.2 - - [01/Jan/2024:10:00:01] "GET /about.html HTTP/1.1" 200 2345
193192.168.1.1 - - [01/Jan/2024:10:00:02] "GET /contact.html HTTP/1.1" 404 0
194192.168.1.3 - - [01/Jan/2024:10:00:03] "POST /api/data HTTP/1.1" 200 5678
195192.168.1.2 - - [01/Jan/2024:10:00:04] "GET /index.html HTTP/1.1" 200 1234
196EOF
197
198 echo "Analyzing log file with process substitution:"
199 echo
200
201 # Compare successful vs failed requests
202 echo "Status code comparison:"
203 paste <(echo "Success (200):") <(grep " 200 " /tmp/access.log | wc -l)
204 paste <(echo "Not Found (404):") <(grep " 404 " /tmp/access.log | wc -l)
205
206 echo
207 echo "Unique IP addresses:"
208 awk '{print $1}' /tmp/access.log | sort -u
209
210 echo
211 echo "Request methods:"
212 awk '{print $6}' /tmp/access.log | tr -d '"' | sort | uniq -c
213
214 # Cleanup
215 rm -f /tmp/access.log
216
217 echo
218}
219
220# --- Demo 7: Named pipe vs process substitution ---
221demo_comparison() {
222 echo "7. Named Pipe vs Process Substitution"
223 echo "--------------------------------------"
224
225 echo "Process substitution creates temporary named pipes:"
226
227 # Show what process substitution looks like
228 echo "Process substitution expands to:"
229 echo " <(cmd) might expand to: /dev/fd/63"
230
231 # Demonstrate by echoing the expansion
232 bash -c 'echo "Expansion: " <(echo test)'
233
234 echo
235 echo "This is equivalent to creating a named pipe, but automatic!"
236
237 echo
238}
239
240# --- Demo 8: Practical example - log analysis ---
241demo_log_analysis() {
242 echo "8. Practical Example: Log Analysis"
243 echo "-----------------------------------"
244
245 # Generate sample log data
246 {
247 echo "2024-01-01 10:00:00 ERROR Database connection failed"
248 echo "2024-01-01 10:00:05 INFO User login: alice"
249 echo "2024-01-01 10:00:10 WARN High memory usage: 85%"
250 echo "2024-01-01 10:00:15 ERROR Timeout on API call"
251 echo "2024-01-01 10:00:20 INFO User login: bob"
252 echo "2024-01-01 10:00:25 ERROR File not found: data.csv"
253 } > /tmp/application.log
254
255 echo "Log file analysis:"
256 echo
257
258 # Count by log level using process substitution
259 echo "Log level counts:"
260 while IFS= read -r level; do
261 count=$(grep "$level" /tmp/application.log | wc -l)
262 printf " %-10s %d\n" "$level" "$count"
263 done < <(awk '{print $3}' /tmp/application.log | sort -u)
264
265 echo
266 echo "Error messages only:"
267 grep "ERROR" /tmp/application.log | sed 's/^/ /'
268
269 # Cleanup
270 rm -f /tmp/application.log
271
272 echo
273}
274
275# Main execution
276main() {
277 demo_diff_commands
278 demo_multiple_inputs
279 demo_avoid_subshell
280 demo_multiple_streams
281 demo_write_process_sub
282 demo_complex_pipeline
283 demo_comparison
284 demo_log_analysis
285
286 echo "=== All Demos Complete ==="
287}
288
289main "$@"