From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FROM,HTML_MESSAGE,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 26513 invoked from network); 8 Nov 2022 18:51:46 -0000 Received: from zero.zsh.org (2a02:898:31:0:48:4558:7a:7368) by inbox.vuxu.org with ESMTPUTF8; 8 Nov 2022 18:51:46 -0000 ARC-Seal: i=1; cv=none; a=rsa-sha256; d=zsh.org; s=rsa-20210803; t=1667933506; b=ZHhygzpb5HccCZmDYsjXsH+iOebqOTUPCCl1jJts0bjIi+lFnV9g1RDTaI628m24Qr5EwCniRo L3I5F3nSFIWnWyWwKZNVaqMxi78vQMpfQ7W+HzPRF0Xm0j/PPbUmKYD/9GL42LsBZm90QWXxXn 8TextBAcPNilzGL55MSwGdvSxuJawPkjKHN3RMmHJ3700d/k/jBd4K38I83VDov/VV+SMpZglW 1epd2mGgG3xEJ7nDqHnuGYDeZ1wy55v+IdQwTP9Wx3J+fMPluGdQJbFVOEWB61JK5tlxawNG2d ZQPWiddyMZ+mEc6z3WQWgzIa3EzuIS6mPr+LGmxpzkRRfQ==; ARC-Authentication-Results: i=1; zsh.org; iprev=pass (mail-vk1-f180.google.com) smtp.remote-ip=209.85.221.180; dkim=pass header.d=gmail.com header.s=20210112 header.a=rsa-sha256; dmarc=pass header.from=gmail.com; arc=none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed; d=zsh.org; s=rsa-20210803; t=1667933506; bh=gAe4BXB5wjhRKYY5K/EubTjO6vYM1ntQpSOwcOf53Fk=; h=List-Archive:List-Owner:List-Post:List-Unsubscribe:List-Subscribe:List-Help: List-Id:Sender:Content-Type:Cc:To:Subject:Message-ID:Date:From:In-Reply-To: References:MIME-Version:DKIM-Signature:DKIM-Signature; b=NeNcQjehH2QmSOmQwziKxJ4WOZBkKcp1qpdYcwerTn9jLL8Q9jJhXftPlIzmAPrgL+rvqQWgak X4Etaw74POSKcZpeeXUu01FCtJAMz4N+Ql3hSaRYZ+veJr0fk55FDIvybUQHL6q4rvax59x0XH gRhsUJLjAMPppQV7ysLe4Y+ZYy5ey+1Ea0ho+fpAPzKNK6zqq9sEo3qM1H2JVW45XYz7txvn49 v359xDc594bbxKC0Mh2tyiSIlPGGHrYS8OsF1DOagWIQIBpXWBRlDbRZ9sKJEqt2gZQlxAwGRU x9KIaGAUrfmBG7nnel394PM9Ub2i3NrfNCZThKaqBByrVQ==; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=zsh.org; s=rsa-20210803; h=List-Archive:List-Owner:List-Post:List-Unsubscribe: List-Subscribe:List-Help:List-Id:Sender:Content-Type:Cc:To:Subject:Message-ID :Date:From:In-Reply-To:References:MIME-Version:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID; bh=NNjmOrwMIwo322godJFvsjCtjuMDeevSujSA3LTDd1c=; b=DRpLQGF9o739J79y4cF89lC/1x ZTJpH7iTrvr7uEI93cwo6wZDj561ILFKP99vNPM86cEuyTt3yJZIa/wz7CxDv06Xvqo/8c/soQ3Ig iLVBAOJa+kC5CELwPOTXL8Q8SD/NOcqCKuitgYKGNPZG10HxgISL8MN5rFAP5xXabanii4XwwTTjT trGxn518FjbvvhZIxKDoOMPmHuZx+9ag4dZBigQwAYj7AdxhfaDBrpPqtQ1AQNqWfjRQtyRodL/DG UP0P+VWJ9FvieLVAcWkTKR70SjUJ8c4Fr8wGNKrDhpMTbDXy8QO/2rbmiCZan08obLySlcY0N76EH adJa2ORA==; Received: by zero.zsh.org with local id 1osThJ-0008rc-KN; Tue, 08 Nov 2022 18:51:45 +0000 Authentication-Results: zsh.org; iprev=pass (mail-vk1-f180.google.com) smtp.remote-ip=209.85.221.180; dkim=pass header.d=gmail.com header.s=20210112 header.a=rsa-sha256; dmarc=pass header.from=gmail.com; arc=none Received: from mail-vk1-f180.google.com ([209.85.221.180]:43661) by zero.zsh.org with esmtps (TLS1.3:TLS_AES_128_GCM_SHA256:128) id 1osTh1-0008Vo-RC; Tue, 08 Nov 2022 18:51:28 +0000 Received: by mail-vk1-f180.google.com with SMTP id m18so9559115vka.10; Tue, 08 Nov 2022 10:51:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=NNjmOrwMIwo322godJFvsjCtjuMDeevSujSA3LTDd1c=; b=BXwRyEkLSWkcDeXI0d0zHU9Z0civ4G0FypvAwkDfQLYnjSI0V93z5Jlhs22vHfeNSE 1p20UEOXz8Du8jxKUdwGAn9IPEw6jsKZPuYgOSEkFepNnZmh4j0oZT6oo6czofJxlxXh CH1lmj03eTW3eh5NsCnlRedyo0V7G4CWkeq+pjPAW7t8t8bI50qFnfRFdWmEQts4ApV3 P08crTL3QTSNa6TUvVHwp1BOrAtxmvMFsOncCynbWpGGG711TpKbnjF4WxLr6aP9Lytj 2/bJtEBxFjFsrC5lvfsXPomxcUiy3QOPwZLaK6rXjWFYHxLt7EvkL8uZk2EjQU+Dy0m1 V4mA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=NNjmOrwMIwo322godJFvsjCtjuMDeevSujSA3LTDd1c=; b=7UGNrTa5S0JqxJqHCnVqBVt4wmWQoC9JsFbzqXT2GiQafPLYPw+x7AJHSJRvA7YLIO i2PYAhnu5CyRarZMrMkMFxgF4m+UpbQpLfkVqThNbSdBz0xAssDQsc5rYsyc+qp7N1zF DqiJx873v6bcT3e6/nv6HMEyRtg0lHEgkhCfExXfWwiRoxA1f80cPs5GS5QEG379g0ww Fa/kEqNoAjz5ezBD7bBYyxLdMtM/yNm9XjBOYwDkARySvbSDXkCWEBp0TjlYlJt4uiDT 6tIK+THxIXUMR+1P+WP+dmZsP0CuQXBJ/c/8G/eHtR6/yutiX6KC9yA9L5Iq5u46q/cn Bc5g== X-Gm-Message-State: ACrzQf0aW8ezGfgg4wSA7jiN5TpXdCDcAglguCUridbSTvpjE1a6GCnK 0l9YAbZXnjWC+HqF6wi8dwEZMqD3oSMKhXdVRLmOOhtinSc= X-Google-Smtp-Source: AMsMyM7GtXGntC9sDo3zoJiy10aBOECvuClBLoj6m85Al/a5UsGu5jlNF7IJA696OaLDd+3yqoG0U7lzVwxcXQ8yKSw= X-Received: by 2002:a05:6122:179a:b0:3b8:592d:c2a with SMTP id o26-20020a056122179a00b003b8592d0c2amr25026109vkf.7.1667933485905; Tue, 08 Nov 2022 10:51:25 -0800 (PST) MIME-Version: 1.0 References: <1edb7786-f0b2-4830-88fa-99a19bda39e2@app.fastmail.com> In-Reply-To: <1edb7786-f0b2-4830-88fa-99a19bda39e2@app.fastmail.com> From: Philippe Altherr Date: Tue, 8 Nov 2022 19:51:14 +0100 Message-ID: Subject: Re: Inconsistent behavior of ERR_EXIT with conditionals To: =?UTF-8?Q?Lawrence_Vel=C3=A1zquez?= Cc: Bart Schaefer , zsh-workers@zsh.org Content-Type: multipart/alternative; boundary="000000000000efba4405ecfa06f9" X-Seq: 50914 Archived-At: X-Loop: zsh-workers@zsh.org Errors-To: zsh-workers-owner@zsh.org Precedence: list Precedence: bulk Sender: zsh-workers-request@zsh.org X-no-archive: yes List-Id: List-Help: , List-Subscribe: , List-Unsubscribe: , List-Post: List-Owner: List-Archive: --000000000000efba4405ecfa06f9 Content-Type: text/plain; charset="UTF-8" Thanks Lawrence for the comparison. It's a bit disappointing to see that Zsh has the worst record :-( Let me try to illustrate with a more concrete example why I think Zsh's behavior could/should be improved. Consider that a first developer wrote the following code: #!/bin/zsh -e > > # ... > > function backup() { > scp $1 $BACKUP_SERVER: > echo $1 >> $BACKUP_LOG > } > > # ... > > backup $file; rm $file > It's arguably not the greatest code but assuming that it's only ever run with ERR_EXIT enabled, it behaves correctly. If "scp" fails, ERR_EXIT is triggered and nothing is logged nor deleted. Some time later a second developer comes, sees the code "backup $file; rm $file", and thinks to himself that this looks dangerous; the file should only be deleted if the backup was successful. Therefore they change the code to "backup $file && rm $file". Unfortunately, this has exactly the opposite effect of the intended one. From then on, if "scp" fails, ERR_EXIT is no longer triggered and the file is logged and deleted. On Tue, Nov 8, 2022, at 12:36 AM, Bart Schaefer wrote: > ERR_EXIT kicks in only when the result is not otherwise checked. Awesome! That's exactly how I would like Zsh to behave. To be slightly more precise, I would formulate it as follow: *ERR_EXIT kicks in if and only if the result is not otherwise checked.* Unfortunately there are several cases where Zsh doesn't behave like that. For example, consider the following commands: { false; true } || true; echo $? > if false; true; then echo 0; else echo 1; fi Both commands print "0". In both commands "false" is part of a condition. However, in both commands, "false" does NOT influence the result of the condition. In fact, in both commands, the result of "false" is NOT checked; replacing it with "true" leads to the exact same result. Therefore, given the specification above, ERR_EXIT should be triggered by "false" in both commands. In the commands above the problem is that Zsh never triggers the ERR_EXIT. Apparently, when Zsh starts evaluating a condition, it no longer enforces the ERR_EXIT option for the whole evaluation of the condition, even if it contains commands whose result is not checked, like the "false" commands in the example above. This is also true if the commands are nested in called functions. The following code also prints "0" instead of existing after the "false" command. function foo() { false; true } > foo || true; echo $? There are other cases where ERR_EXIT is triggered but fails to propagate. A major offender in that regard is the following code: local var=$(false); echo $? In this case "false" triggers an ERR_EXIT but it only exits the sub-shell of the command substitution. For some reason, the local variable assignment ignores the exit status of the command substitution and always returns a zero exit status. Therefore the main shell does NOT exit and the command prints "0". Interestingly, in the following almost identical code, "false" triggers an ERR_EXIT that also exits the main shell: local var; var=$(false); echo $? However, having to systematically use this style is rather cumbersome. Furthermore it's not even foolproof. Indeed, if there are multiple command substitutions, the assignment returns the exit status of the last one. Thus, the following command does NOT exit and prints "0": local var; var=$(false)$(true); echo $? Since I really really want Zsh to behave as described above, I implemented zabort , which configures a ZERR trap to exit the current shell and all parent shells whenever an ERR_EXIT is triggered. It also prints a nice stack trace to the command that failed. This fixes the problem for all cases where the ERR_EXIT isn't propagated, like in the variable assignments above. However, the problem of the conditional expressions remains because in that case no ERR_EXIT (and no ZERR trap) is ever triggered. Fixing this seems only possible by changing the implementation of Zsh. Here is an example using zabort: #!/bin/zsh > > . zabort.zsh > > function log() { echo $@ 1>&2 } > > function f1() { false; log f1 } > function f2() { : $(f1); log f2 } > function f3() { local v3=$(f2); log f3 } > function f4() { v4=$(f3)$(true); log f4 } > function f5() { f4; log f5 } > > f5 > And here is what it prints: Command unexpectedly exited with the non-zero status 1. > at abort-example.zsh:7(abort) > at abort-example.zsh:8(f1) > at abort-example.zsh:9(f2) > at abort-example.zsh:10(f3) > at abort-example.zsh:11(f4) > at abort-example.zsh:13(f5) *Is there any chance that Zsh could be changed to more closely follow the specification above? * I'm mainly interested in a fix for the conditional expressions but fixes for the other issues would also be nice. It would be awesome if "zsh -e" behaved as specified above in all cases. If needed, I could look into implementing some of the fixes myself. However, before I invest into this, I would prefer to know whether you would be open to such changes. Philippe --000000000000efba4405ecfa06f9 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Thanks=C2=A0Lawrence for the=C2=A0comparison. It'= s a bit disappointing to see that Zsh has the worst record :-(
Let me try to illustrate with a more concrete example why I th= ink Zsh's behavior could/should be improved. Consider that a first deve= loper wrote the following code:

#!/bin/zsh -e

# ...

function backu= p() {
=C2=A0 =C2=A0 scp $1 $BACKUP_SERVER:
=C2=A0 =C2=A0 echo $1 >= > $BACKUP_LOG
}

# ...

backup $file; rm $file

It's arguably not the greatest code but assum= ing that it's only ever run with ERR_EXIT enabled, it behaves correctly= . If "scp" fails, ERR_EXIT is triggered and nothing is logged nor= deleted.

Some time later a second developer comes= , sees the code "backup $file; rm $file", and thinks to himself t= hat this looks dangerous; the file should only be deleted if the backup was= successful. Therefore they change the code to "backup $file &&= ; rm $file". Unfortunately, this has exactly the opposite effect of th= e intended one. From then on, if "scp" fails, ERR_EXIT is no long= er triggered and the file is logged and deleted.

=

On Tue, Nov 8, 2022, at 1= 2:36 AM, Bart Schaefer wrote:
ERR_EXIT kicks in only when the result is not otherwise check= ed.

Awesome! That's exactly= how I would like Zsh to behave. To be slightly more precise, I would formu= late it as follow:

ERR_EXIT kicks in if and=C2=A0only if t= he result=C2=A0is not otherwise checked.

Unfortunately there are several cases where Zsh doesn't behave=C2=A0= like that. For example, consider the following commands:

{ false; true } || true; echo= $?
if false; true; then echo 0; else echo 1; fi=C2=A0
=
Both commands print "0". In both commands "fa= lse" is part of a condition. However, in both=C2=A0commands, "fal= se" does NOT influence the result of the condition. In fact, in both c= ommands, the result of "false" is NOT checked; replacing it with = "true" leads to the exact same result. Therefore, given the speci= fication above, ERR_EXIT should be triggered by "false" in both c= ommands.

In the commands above the problem is= that Zsh never triggers the ERR_EXIT. Apparently, when Zsh starts evaluati= ng a condition, it no longer enforces=C2=A0the ERR_EXIT option for the whol= e evaluation of the condition, even if it contains commands whose result is= not checked, like the "false" commands in the example above. Thi= s is also true if the commands are nested in called functions. The followin= g code also prints "0" instead of existing after the "false&= quot; command.

function foo() { false; true }
foo || true; echo $?

There are other cases where ERR_EXIT is trigger= ed but fails to propagate. A major offender in that regard is the following= code:

local var; var=3D$(false); echo $?

However, having to systematically use this style is = rather cumbersome. Furthermore it's not even foolproof. Indeed, if ther= e are multiple command substitutions, the assignment returns the exit statu= s of the last one. Thus, the following command does NOT exit and prints &qu= ot;0":

local var; var=3D$(false)$(true); echo $?

=
Since I really really want Zsh to behave as described abov= e, I implemented=C2=A0zabort, which configures a ZERR trap to exit the= current shell and all parent shells whenever an ERR_EXIT is triggered. It = also prints a nice stack trace to the command that failed. This fixes the p= roblem for all cases where the ERR_EXIT isn't propagated, like in the v= ariable assignments above. However, the problem of the conditional expressi= ons remains because in that case no ERR_EXIT (and no ZERR trap) is ever tri= ggered. Fixing this seems only possible by changing the implementation of Z= sh.

Here is an example using zabort:
#!/bin/zsh

= . zabort.zsh

function log() { echo $@ 1>&2 }

function = f1() { false; log f1 }
function f2() { : $(f1); log f2 }
function f3(= ) { local v3=3D$(f2); log f3 }
function f4() { v4=3D$(f3)$(true); log f4= }
function f5() { f4; log f5 }

f5

And here is what it prints:

Command unexpectedly exited with the non-zero = status 1.
at abort-example.zsh:7(abort)
at abort-example.zsh:8(f1)at abort-example.zsh:9(f2)
at abort-example.zsh:10(f3)
at abort-exam= ple.zsh:11(f4)
at abort-example.zsh:13(f5)

Is there any chance that Zsh could be changed to more closely follow = the specification above?=C2=A0

I'm mainly = interested in a fix for the conditional expressions but fixes for the other= issues would also be nice. It would be awesome if "zsh -e" behav= ed as specified above in all cases.

If needed, I c= ould look into implementing some of the fixes myself. However, before I inv= est into this, I would prefer to know whether you would be open to such cha= nges.

Philippe

--000000000000efba4405ecfa06f9--