0

The following script

#!/bin/sh -e


wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.32-r0/glibc-2.32-r0.apk
apk add glibc-2.32-r0.apk && rm glibc-2.32-r0.apk
sleep 1

run on an alpine:3.16 image succeeds, while there is definitely an error in the apk add glibc-2.32-r0.apk command.

/ # ./script.sh 
Connecting to github.com (140.82.121.3:443)
Connecting to objects.githubusercontent.com (185.199.108.133:443)
saving to 'glibc-2.32-r0.apk'
glibc-2.32-r0.apk    100% |**************************************************************************************************************************************************************************************************| 4331k  0:00:00 ETA
'glibc-2.32-r0.apk' saved
(1/1) Installing glibc (2.32-r0)
ERROR: glibc-2.32-r0: trying to overwrite etc/nsswitch.conf owned by alpine-baselayout-data-3.2.0-r23.
1 error; 44 MiB in 20 packages
/ # echo $?
0

Why isn't the && operator in the following line

apk add glibc-2.32-r0.apk && rm glibc-2.32-r0.apk

work as expected?

Robert
  • 39,162
  • 17
  • 99
  • 152
pkaramol
  • 16,451
  • 43
  • 149
  • 324
  • 2
    Branching on output of an operation marks it as "checked" so `set -e` no longer applies. (This is one of the many, many reasons why `set -e` is unreliable _in general_ and should be avoided to favor explicit error handling instead) – Charles Duffy Jan 20 '23 at 23:39
  • By "branching on output" you mean the usage of `&&` ? – pkaramol Jan 20 '23 at 23:42
  • Yes. See [the exercises section of BashFAQ #105](https://mywiki.wooledge.org/BashFAQ/105#Exercises), if not also the longer preceding text. – Charles Duffy Jan 20 '23 at 23:43
  • ...for even more fun (leaning into how different implementations of `/bin/sh` have incompatible corner cases around `set -e` behavior), see also https://www.in-ulm.de/~mascheck/various/set-e/ – Charles Duffy Jan 20 '23 at 23:43
  • Btw.: `sh` ([Bourne-shell](https://en.wikipedia.org/wiki/Bourne_shell)) is usally not `bash` ([Bourne-again shell](https://en.wikipedia.org/wiki/Bash_(Unix_shell))). – Cyrus Jan 20 '23 at 23:44
  • (and it's _definitely_ not bash in alpine) – Charles Duffy Jan 20 '23 at 23:44
  • @Cyrus, ...mind, _grumph_ about calling what's typically POSIX sh (following a 1990s-era standard) "Bourne" (a specific 1970s-era implementation). "Bourne again" is a cute/clever name, but... not all that accurate. (When I started my career as a build engineer around the end of the 90s, GNU autoconf distinguished between Bourne sh and POSIX sh by checking whether `^` was treated as a pipe character) – Charles Duffy Jan 20 '23 at 23:45
  • I guess I shouldn't bother asking why [this](https://pastebin.com/N5efCgKx) in the exact same OS fails as expected...In this case the "branching" is not marked as "checked"... – pkaramol Jan 20 '23 at 23:51
  • @pkaramol, add any additional successful command at the end of that script -- `echo` , or `true`, or so forth -- and it'll no longer have an overall nonzero exit status. What you're seeing is just the residual `$?` value, as opposed to `set -e` kicking in. – Charles Duffy Jan 20 '23 at 23:51
  • @pkaramol: The behavior shown by you in your question is not comprehensible for me with and without `-e`. – Cyrus Jan 20 '23 at 23:55
  • (I _am_ assuming here that your real script has some additional content beyond the failure point shown, because -- as Cyrus says -- the stated behavior doesn't make sense otherwise) – Charles Duffy Jan 20 '23 at 23:56
  • ...ah, there we are; with the `sleep 1` we see how you have `$?` set to 0 for the last line. :) – Charles Duffy Jan 20 '23 at 23:57
  • 1
    I had forgotten just a `sleep 1` at the end of the script – pkaramol Jan 20 '23 at 23:58
  • so the exit code of the script is just its last line, **despite** the usage of `-e`? that's the whole story right? – pkaramol Jan 20 '23 at 23:59
  • 1
    Yup. I've amended my answer to confirm your above statement explicitly. – Charles Duffy Jan 21 '23 at 00:01
  • 1
    damn did I open a can of worms ... didn't see that coming...I am still trying to recover from [this](https://www.in-ulm.de/~mascheck/various/set-e/) nigthmare-ish thing... – pkaramol Jan 21 '23 at 00:02
  • 2
    I really do recommend avoiding the nightmare altogether by leaving `set -e` off and using `|| exit` or `|| return` as appropriate after each command where a failure is expected to cause the active script or function to end. – Charles Duffy Jan 21 '23 at 00:04

1 Answers1

3

Because the shell can't distinguish between a 1 returned because of a failure and a 1 returned to successfully indicate false, any case where you're branching on a command disables set -e behavior for the duration of that command.

Thus, because the && rm glibc-2.32-r0.apk happens conditionally on whether the apk command succeeds, that apk command is "checked", so set -e does not apply for it; the sleep at the last line is executed, and its exit status (of 0) is returned for the script as a whole.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441