| 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 | "$@"
|