1 | #!/usr/bin/env bash
|
2 | #
|
3 | # Usage:
|
4 | # demo/background-status.sh <function name>
|
5 |
|
6 | set -o nounset
|
7 | #set -o pipefail
|
8 | #set -o errexit # wait waill fail with this
|
9 |
|
10 | do_some_work() {
|
11 | sleep 0.2
|
12 | exit $1
|
13 | }
|
14 |
|
15 | wait_pids() {
|
16 | { sleep 0.1; exit 5; } &
|
17 | pid1=$!
|
18 |
|
19 | do_some_work 6 &
|
20 | pid2=$!
|
21 |
|
22 | { sleep 0.3; exit 7; } &
|
23 | pid3=$!
|
24 |
|
25 | do_some_work 8 &
|
26 | pid4=$!
|
27 |
|
28 | echo "Waiting for PIDs $pid1 $pid2 $pid3"
|
29 |
|
30 | wait $pid1; echo $?
|
31 | wait $pid2; echo $?
|
32 | wait $pid3; echo $?
|
33 | wait $pid4; echo $?
|
34 |
|
35 | echo 'Done'
|
36 | }
|
37 |
|
38 | # NOTE: dash/mksh/zsh all lack the -n option. That seems like an oversight.
|
39 | wait_next() {
|
40 | { sleep 0.1; exit 8; } &
|
41 | pid1=$!
|
42 |
|
43 | { sleep 0.2; exit 9; } &
|
44 | pid2=$!
|
45 |
|
46 | { sleep 0.3; exit 10; } &
|
47 | pid3=$!
|
48 |
|
49 | wait -n; echo $?
|
50 | wait -n; echo $?
|
51 | wait -n; echo $?
|
52 |
|
53 | echo 'Done'
|
54 | }
|
55 |
|
56 | both() {
|
57 | wait_pids
|
58 | wait_next
|
59 | }
|
60 |
|
61 | # ERROR: says "no job control"
|
62 | job_control() {
|
63 | { sleep 0.1; exit 8; } &
|
64 | fg
|
65 | }
|
66 |
|
67 | # ERROR: says "no job control"
|
68 | job_control2() {
|
69 | { sleep 0.1; exit 8; } &
|
70 | %1
|
71 | }
|
72 |
|
73 | jobs_list() {
|
74 | local start_what=${1:-process} # or pipeline
|
75 | local wait_style=${2:-all}
|
76 |
|
77 | # Works with: dash/ash, bash mksh yash
|
78 | # Does NOT work with zsh
|
79 | #
|
80 | # OSH:
|
81 | # - jobs list looks weird
|
82 | # - extra stuff on stderr
|
83 | # but otherwise it works
|
84 |
|
85 | pids_down=''
|
86 | pids_up=''
|
87 | for i in 3 2 1; do
|
88 | case $start_what in
|
89 | process)
|
90 | { sleep $i; echo i=$i; exit $i; } &
|
91 | pid=$!
|
92 | ;;
|
93 | pipeline)
|
94 | sleep $i | echo i=$i | ( exit $i ) &
|
95 | pid=$!
|
96 | ;;
|
97 | *)
|
98 | echo "Invalid arg '$start_what'" >&2
|
99 | return 1
|
100 | ;;
|
101 | esac
|
102 |
|
103 | pids_down="$pids_down $pid"
|
104 | pids_up="$pid $pids_up"
|
105 | echo "--- started $i"
|
106 | jobs -l
|
107 | echo
|
108 |
|
109 | done
|
110 | echo $pids_down
|
111 | echo $pids_up
|
112 |
|
113 | pid_list=''
|
114 | case $wait_style in
|
115 | pass_all)
|
116 | wait $pids_down
|
117 | ;;
|
118 | all)
|
119 | wait
|
120 | ;;
|
121 | next)
|
122 | for i in $pids_down; do
|
123 | wait -n
|
124 | echo "--- wait -n --> status=$?"
|
125 | done
|
126 | ;;
|
127 | down_one)
|
128 | pid_list=$pids_down
|
129 | ;;
|
130 | up_one)
|
131 | pid_list=$pids_up
|
132 | ;;
|
133 | down_jobs)
|
134 | pid_list='%3 %2 %1'
|
135 | ;;
|
136 | up_jobs)
|
137 | pid_list='%1 %2 %3'
|
138 | ;;
|
139 | none)
|
140 | echo 'Not waiting'
|
141 |
|
142 | # When can we remove the (pid -> status) mapping?
|
143 | # If we don't wait, We never do. The shell exists, and the processes
|
144 | # keep going!
|
145 | # So the actual 'wait' command is the thing that removes records, NOT
|
146 | # process death.
|
147 |
|
148 | # Some dummy processes
|
149 | for i in 1 2 3; do
|
150 | sleep 0.01
|
151 | done
|
152 | ;;
|
153 | *)
|
154 | echo "Invalid wait style '$wait_style'" >& 2
|
155 | wait
|
156 | return 1
|
157 | esac
|
158 |
|
159 | case $wait_style in
|
160 | down_*|up_*)
|
161 | for p in $pid_list; do
|
162 | wait $p
|
163 | echo "--- pid $p --> status $?"
|
164 |
|
165 | # Hm dash/ash, and yash have a SIMILAR bug as OSH - status can be 127,
|
166 | # but only jobs -l
|
167 | #
|
168 | # bash and mksh do it correctly
|
169 | # zsh is messed up in other ways
|
170 | #
|
171 | # So this means that our way of handling failure only works in BASH/mksh,
|
172 | # not in POSIX shell! There are similar problems with xargs. Need a
|
173 | # blog post about this.
|
174 | jobs -l # problem: this can "lose" exit codes
|
175 | done
|
176 | ;;
|
177 | esac
|
178 |
|
179 | echo '--- Jobs after waiting'
|
180 | jobs -l
|
181 | echo '---'
|
182 |
|
183 | # the status is the last one, which is 2
|
184 | echo status=$?
|
185 | }
|
186 |
|
187 | stopped_process() {
|
188 | sleep 5 &
|
189 | local pid=$!
|
190 |
|
191 | set -x
|
192 | sleep 0.1
|
193 | kill -STOP $pid
|
194 |
|
195 | #kill -TERM $pid
|
196 |
|
197 | # wait is only for exiting
|
198 | wait $pid
|
199 | echo status=$?
|
200 | }
|
201 |
|
202 | # from test/process-table-portable.sh
|
203 | readonly PS_COLS='pid,ppid,pgid,sid,tpgid,comm'
|
204 |
|
205 | last_id() {
|
206 | sleep 1 | cat &
|
207 |
|
208 | # But what's the progress group leader?
|
209 | #
|
210 | # In non-interactive shell, it's the shell itself
|
211 | # In an interactive shell, it's the FIRST part of the pipeline.
|
212 | #
|
213 | # This is super confusing.
|
214 | # So when you do wait $! on the LAST part of the pipeline, are you waiting on
|
215 | # an individual process, or a JOB that's the pipeline. Gah
|
216 | ps -o $PS_COLS
|
217 |
|
218 | # 6277 is the last part of the pipeline! You can 'wait' on it?
|
219 | pid=$!
|
220 | jobs -l
|
221 | echo pid=$pid
|
222 | set -x
|
223 | wait $pid
|
224 |
|
225 | }
|
226 |
|
227 | "$@"
|