OILS / demo / hard-coded-descriptors.sh View on Github | oilshell.org

140 lines, 94 significant
1#!/usr/bin/env bash
2#
3# Nice examples from blog post feedback.
4#
5# Usage:
6# ./hard-coded-descriptors.sh <function name>
7
8set -o nounset
9set -o pipefail
10set -o errexit
11
12# https://lobste.rs/s/bqftd6/avoid_directly_manipulating_file
13#
14# - "One thing that I have found non-{0,1,2} FDs useful for however is when
15# tools (e.g. gpg) take a --passphrase-fd argument, useful for when you are
16# also redirecting some other thing into stdin."
17# - "Some protocols (like djb’s checkpassword or doit) rely on specific file
18# descriptors"
19
20# https://www.reddit.com/r/bash/comments/6td8j2/avoid_directly_manipulating_file_descriptors_in/
21
22interactive-stdin() {
23 local path=$0 # This script
24 while read line <&"$fd" ; do
25 echo "[$line]"
26 read -p "Continue? " ans
27 [[ "$ans" != yes ]] && exit 1
28 done {fd}< $path
29}
30
31# Here stdin conflicts
32interactive-stdin-bad() {
33 local path=$0 # This script
34 while read line; do
35 echo "[$line]"
36 read -p "Continue? " ans
37 [[ "$ans" != yes ]] && exit 1
38 done < $path
39}
40
41# Alternative way without explicit descriptors.
42interactive-stdin-alternative() {
43 local path=$0 # This script
44 local tty=$(tty) # e.g. /dev/pts/23
45 while read line; do
46 echo "[$line]"
47 read -p "Continue? " ans < $tty
48 [[ "$ans" != yes ]] && exit 1
49 done < $path
50}
51
52# https://www.reddit.com/r/oilshell/comments/6tch5v/avoid_directly_manipulating_file_descriptors_in/
53
54log-dates() {
55 exec {fd}> >(while IFS= read -r line; do printf "[%s] %s\n" "$(date)" "$line"; done)
56 echo "Hello" >&"$fd"
57 echo "Goodbye" >&"$fd"
58}
59
60tee-like() {
61 local log=_tmp/tee-like.txt
62
63 >$log # truncate
64
65 exec {fd}> >(while IFS= read -r line; do printf "%s\n" "$line"; printf "%s\n" "$line" >&3; done 3>>"$log")
66 echo "Hello" >&"$fd"
67 echo "Goodbye" >&"$fd"
68
69 echo
70 echo "Contents of $log:"
71 cat $log
72}
73
74pipe-stderr() {
75 local log=_tmp/pipe-stderr.log
76 set +o errexit
77
78 ls -l /etc/passwd not_a_file 3>&1 1>&2 2>&3 \
79 | awk '{print "ERROR:" $0}' >$log
80
81 # Another idiom, this seems wrong because of 3>&-
82 exec 3>&1
83 ls -l /etc/passwd 'not_a_file_2' 2>&1 >&3 3>&- \
84 | awk '{print "ERROR:" $0}' >>$log
85 exec 3>&-
86
87 echo
88 echo "Contents of $log:"
89 cat $log
90}
91
92_zip() {
93 ( # use a subshell to ensure the FD changes don't affect the caller
94 exec 4<"$1" 5<"$2"
95 while IFS="" read -u 4 a && read -u 5 b; do
96 printf "%s %s\n" "$a" "$b"
97 done
98 )
99}
100
101zip-demo() {
102 seq 1 5 > _tmp/left.txt
103 seq 5 10 > _tmp/right.txt
104 _zip _tmp/{left,right}.txt
105}
106
107
108_work() {
109 local id=$1
110 echo "Job $id"
111 for i in 1 2 3; do
112 echo "... $i ..."
113 sleep 0.2
114 done
115}
116
117# man flock
118_flock() {
119 (
120 flock -n 9 || {
121 echo "Another job is already running; aborting"
122 exit 1
123 }
124 "$@"
125
126 ) 9>_tmp/mylockfile
127}
128
129flock-demo() {
130 _work A
131 _work B
132
133 # Instead of running
134 $0 _flock _work C &
135 $0 _flock _work D & # One is already running
136 wait
137 wait
138}
139
140"$@"