Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > muc.lists.netbsd.tech.userlevel > #11704 > unrolled thread

set -e again

Started byDavid Holland <dholland-tech@netbsd.org>
First post2026-01-14 01:18 +0000
Last post2026-01-14 21:09 +0700
Articles 9 — 6 participants

Back to article view | Back to muc.lists.netbsd.tech.userlevel


Contents

  set -e again David Holland <dholland-tech@netbsd.org> - 2026-01-14 01:18 +0000
    Re: set -e again "Greg A. Woods" <woods@planix.ca> - 2026-01-13 18:14 -0800
    Re: set -e again Mouse <mouse@Rodents-Montreal.ORG> - 2026-01-13 21:30 -0500
    Re: set -e again Crystal Kolipe <kolipe.c@exoticsilicon.com> - 2026-01-14 10:28 +0000
      Re: set -e again Mouse <mouse@Rodents-Montreal.ORG> - 2026-01-14 07:41 -0500
        Re: set -e again Crystal Kolipe <kolipe.c@exoticsilicon.com> - 2026-01-14 13:46 +0000
      Re: set -e again Robert Elz <kre@munnari.OZ.AU> - 2026-01-14 21:22 +0700
    Re: set -e again RVP <rvp@SDF.ORG> - 2026-01-14 13:39 +0000
    Re: set -e again Robert Elz <kre@munnari.OZ.AU> - 2026-01-14 21:09 +0700

#11704 — set -e again

FromDavid Holland <dholland-tech@netbsd.org>
Date2026-01-14 01:18 +0000
Subjectset -e again
Message-ID<aWbu963HcQMeCm2M@netbsd.org>
I discovered the following today.

This:
   set -e
   f() {
       echo foo
       false
       echo bar
   }
   f

as one might expect, prints "foo" and exits.

However, this:
   set -e
   f() {
       echo foo
       false
       echo bar
   }
   f || echo baz
   echo buzz

prints "foo", "bar", "buzz", and continues. Furthermore, all the
shells I have in easy reach agree on it.

This seems wrong - the exit status of the f is guarded, but the false
is not. But also, the fact that everybody agrees makes me think it's
probably the agreed result of the last round of POSIX wrangling over
the -e definition some years back.

Is this intended, and is there a way to get the user's intended
behavior of exit on unchecked failure back? (E.g. is there a different
set to get functions to return on unchecked failure that might cover
this?)

-- 
David A. Holland
dholland@netbsd.org

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [next] | [standalone]


#11705

From"Greg A. Woods" <woods@planix.ca>
Date2026-01-13 18:14 -0800
Message-ID<m1vfqPL-00Mo5fC@more.local>
In reply to#11704

[Multipart message — attachments visible in raw view] — view raw

At Wed, 14 Jan 2026 01:18:47 +0000, David Holland <dholland-tech@netbsd.org> wrote:
Subject: set -e again
>
> I discovered the following today.
>
> This:
>    set -e
>    f() {
>        echo foo
>        false
>        echo bar
>    }
>    f
>
> as one might expect, prints "foo" and exits.
>
> However, this:
>    set -e
>    f() {
>        echo foo
>        false
>        echo bar
>    }
>    f || echo baz
>    echo buzz
>
> prints "foo", "bar", "buzz", and continues. Furthermore, all the
> shells I have in easy reach agree on it.

That makes perfect sense to me.

Note that if the function invocation is not "guarded" by testing its
exit status then execution is terminated after the false statement just
as one would also expect.

Shell functions are effectively macros.

Replace the invocation of f() with its "value" and it now looks like:

	$ set -e
	$ { echo foo; false; echo bar; } || echo baz; echo buzz
	foo
	bar
	buzz
	$ echo $?
	0

Perhaps another way to think about it is that the part wrapped in
brackets is a compound command, and indeed the manual concurs.

A function definition is most simply "f() command", and of course the
"command" part can be either a simple command or a list (i.e. a form of
compound command).

So since the compound command is followed by an operator that tests its
status, the whole compound command is executed in the guarded
environment.

The manual also says:  "The function completes after having executed
_command_ with [[its]] exit status set to the status returned by
_command_." though obviously that statement doesn't take into account an
un-guarded execution of a function in a shell with errexit("set -e") set.

> Is this intended, and is there a way to get the user's intended
> behavior of exit on unchecked failure back? (E.g. is there a different
> set to get functions to return on unchecked failure that might cover
> this?

I would definitely say it is/was intended.

Perhaps as an extension the shell could override the guard if a function
definition includes a call to "set -e" within it.

--
					Greg A. Woods <gwoods@acm.org>

Kelowna, BC     +1 250 762-7675           RoboHack <woods@robohack.ca>
Planix, Inc. <woods@planix.com>     Avoncote Farms <woods@avoncote.ca>

[toc] | [prev] | [next] | [standalone]


#11706

FromMouse <mouse@Rodents-Montreal.ORG>
Date2026-01-13 21:30 -0500
Message-ID<202601140230.VAA26490@Stone.Rodents-Montreal.ORG>
In reply to#11704
> I discovered the following today.

> [...sh set -e oddity...]

> Furthermore, all the shells I have in easy reach agree on it.

So do 5.2's and even 1.4T's sh.

I also tried changing

	echo foo

to

	echo "foo $-"

to see if perhaps -e was getting turned off inside the function.
Apparently not; I get "foo e" regardless of whether f's return is
checked or not.

Also, interestingly, adding the three lines "local -", "set +e", and
"set -e" to the beginning of f() does not change this.

> This seems wrong

I concur, especially since I too have been unable to find any way to
get the effect of set -e within f when f is the LHS of ||.  { }
doesn't help.  Neither does ( ).  Neither does setting -e after
defining f.  Neither does setting e only within f, not in the main
script (!!).  (These tests done with 5.2's sh.)

> But also, the fact that everybody agrees makes me think it's probably
> the agreed result of the last round of POSIX wrangling over the -e
> definition some years back.

If so, *quite* some years back, in that 1.4T's sh agrees.  Unless
Bourne's own work did this and everyone else felt it necessary to
ensure compatibility.

/~\ The ASCII				  Mouse
\ / Ribbon Campaign
 X  Against HTML		mouse@rodents-montreal.org
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [prev] | [next] | [standalone]


#11707

FromCrystal Kolipe <kolipe.c@exoticsilicon.com>
Date2026-01-14 10:28 +0000
Message-ID<aWdv1ZpNO4v-29BG@exoticsilicon.com>
In reply to#11704
On Wed, Jan 14, 2026 at 01:18:47AM +0000, David Holland wrote:
> I discovered the following today.
> 
> This:
>    set -e
>    f() {
>        echo foo
>        false
>        echo bar
>    }
>    f
> 
> as one might expect, prints "foo" and exits.
> 
> However, this:
>    set -e
>    f() {
>        echo foo
>        false
>        echo bar
>    }
>    f || echo baz
>    echo buzz
> 
> prints "foo", "bar", "buzz", and continues. Furthermore, all the
> shells I have in easy reach agree on it.
> 
> This seems wrong - the exit status of the f is guarded, but the false
> is not. But also, the fact that everybody agrees makes me think it's
> probably the agreed result of the last round of POSIX wrangling over
> the -e definition some years back.
> 
> Is this intended, and is there a way to get the user's intended
> behavior of exit on unchecked failure back? (E.g. is there a different
> set to get functions to return on unchecked failure that might cover
> this?)

Does this achieve what you want?

#!/bin/sh
set -e
f() {
	echo foo
	false
	echo bar
}
f
[ $? ] || echo baz
echo buzz

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [prev] | [next] | [standalone]


#11708

FromMouse <mouse@Rodents-Montreal.ORG>
Date2026-01-14 07:41 -0500
Message-ID<202601141241.HAA04556@Stone.Rodents-Montreal.ORG>
In reply to#11707
>> [...set -e vs shell functions...]

> Does this achieve what you want?

> #!/bin/sh
> set -e
> f() {
> 	echo foo
> 	false
> 	echo bar
> }
> f
> [ $? ] || echo baz
> echo buzz

I'm not the original poster, but it does not achieve what I would
expect, which is

foo
baz
buzz

That is, I would expect/want -e to cause the function to return showing
failure.  Instead, it either does nothing or takes down the whole
shell.

This does achieve it, not surprisingly, but it's an ugly kludge; I
would say that neither the nested sh nor the nested set -e should be
necessary:

set -e
f() {
sh -c '
	set -e
	echo "foo $-"
	false
	echo bar
'
}
f || echo baz
echo buzz

/~\ The ASCII				  Mouse
\ / Ribbon Campaign
 X  Against HTML		mouse@rodents-montreal.org
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [prev] | [next] | [standalone]


#11710

FromCrystal Kolipe <kolipe.c@exoticsilicon.com>
Date2026-01-14 13:46 +0000
Message-ID<aWeeKG8ziRu40n77@exoticsilicon.com>
In reply to#11708
On Wed, Jan 14, 2026 at 07:41:41AM -0500, Mouse wrote:
> >> [...set -e vs shell functions...]
> 
> > Does this achieve what you want?
> 
> > #!/bin/sh
> > set -e
> > f() {
> > 	echo foo
> > 	false
> > 	echo bar
> > }
> > f
> > [ $? ] || echo baz
> > echo buzz
> 
> I'm not the original poster, but it does not achieve what I would
> expect, which is
> 
> foo
> baz
> buzz
> 
> That is, I would expect/want -e to cause the function to return showing
> failure.  Instead, it either does nothing or takes down the whole
> shell.

Ah, OK, the way I understood the OP was that 'false' taking down the whole
script was the desired/expected behaviour, but that that was not happening in
the second example with the conditional testing the exit staus of f().

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [prev] | [next] | [standalone]


#11712

FromRobert Elz <kre@munnari.OZ.AU>
Date2026-01-14 21:22 +0700
Message-ID<5133.1768400569@jacaranda.noi.kre.to>
In reply to#11707
    Date:        Wed, 14 Jan 2026 10:28:37 +0000
    From:        Crystal Kolipe <kolipe.c@exoticsilicon.com>
    Message-ID:  <aWdv1ZpNO4v-29BG@exoticsilicon.com>

  | Does this achieve what you want?
  |
  | #!/bin/sh
  | set -e
  | f() {
  | 	echo foo
  | 	false
  | 	echo bar
  | }
  | f
  | [ $? ] || echo baz
  | echo buzz

I doubt it.

[ $? ] is always true.   $? is never '' which is the only way that
could be false.   If it were [ $? -ne 0 ] it would be closer.

But worse than that, the f call is now subject to the -e setting,
which it wasn't before - if the real f() (whatever caused the test
case to be created in the first place - no-one really wants a script
which just outputs foo bar baz buzz in this way - happened to end in
a "return 1" or similar (we must assume that is intended, or the
"|| echo ..." after its call wouldn't be there) is now going to make
the script exit, as f has failed.

Using -e is just not worth the bother, it is a nuisance, always has
been.   But it makes writing simple makefiles simple - it is what
causes the make script to stop as soon as one of the commands in it
fails.

kre



--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [prev] | [next] | [standalone]


#11709

FromRVP <rvp@SDF.ORG>
Date2026-01-14 13:39 +0000
Message-ID<5fd0838f-4677-4810-6e5d-7cda0be107b0@SDF.ORG>
In reply to#11704
On Wed, 14 Jan 2026, David Holland wrote:

>   set -e
>   f() {
>       echo foo
>       false
>       echo bar
>   }
>   f || echo baz
>   echo buzz
>
> prints "foo", "bar", "buzz", and continues. Furthermore, all the
> shells I have in easy reach agree on it.
>
> This seems wrong - the exit status of the f is guarded, but the false
> is not. But also, the fact that everybody agrees makes me think it's
> probably the agreed result of the last round of POSIX wrangling over
> the -e definition some years back.
>
> Is this intended, and is there a way to get the user's intended
> behavior of exit on unchecked failure back? (E.g. is there a different
> set to get functions to return on unchecked failure that might cover
> this?)
>

There's a way, but, it a) involves running functions in a subshell, and b)
set +e/-e fiddling before/after each function:

```
set -e		# orig.
f() {
     set -e	# for func.
     echo foo
     false
     echo bar
}
set +e		# for every func.
(f); rc=$?
set -e
[ $rc -eq 0 ] || echo baz
echo buzz
```

-RVP

--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [prev] | [next] | [standalone]


#11711

FromRobert Elz <kre@munnari.OZ.AU>
Date2026-01-14 21:09 +0700
Message-ID<17878.1768399781@jacaranda.noi.kre.to>
In reply to#11704
    Date:        Wed, 14 Jan 2026 01:18:47 +0000
    From:        David Holland <dholland-tech@netbsd.org>
    Message-ID:  <aWbu963HcQMeCm2M@netbsd.org>

  | I discovered the following today.

I would have expected you to be aware of all this already.

  | This seems wrong - the exit status of the f is guarded, but the false
  | is not.

Doesn't matter, it is executing in a place to the left of a || hence
the setting of -e is ignored.

  | Is this intended, and is there a way to get the user's intended
  | behavior of exit on unchecked failure back?

It is, and no, there isn't.   Use of -e in anything other than a list of
simple commands, one after the other, almost always runs into problems.
It was originally intended for (old style) makefile use, not for lazy
script writers.

Simply explicitly test every command's status which matters, explicitly,
and everything will work as you intend.   Don't use -e, almost ever.
(And -u isn't a lot better, but at least that one is more predictable).

kre


--
Posted automagically by a mail2news gateway at muc.de e.V.
Please direct questions, flames, donations, etc. to news-admin@muc.de

[toc] | [prev] | [standalone]


Back to top | Article view | muc.lists.netbsd.tech.userlevel


csiph-web