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