From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29405 invoked by alias); 1 Nov 2014 19:15:07 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 33591 Received: (qmail 6286 invoked from network); 1 Nov 2014 19:15:04 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 X-Originating-IP: [80.3.229.105] X-Spam: 0 X-Authority: v=2.1 cv=RdIeCjdv c=1 sm=1 tr=0 a=uz1KDxDNIq33yePw376BBA==:117 a=uz1KDxDNIq33yePw376BBA==:17 a=NLZqzBF-AAAA:8 a=IkcTkHD0fZMA:10 a=pGLkceISAAAA:8 a=lGe7qETsoBTcx6xlRzsA:9 a=QEXdDO2ut3YA:10 Date: Sat, 1 Nov 2014 19:15:00 +0000 From: Peter Stephenson To: zsh-workers@zsh.org Subject: Re: Bug report: Strange behaviour of $SHLVL in subshell. Message-ID: <20141101191500.5f4e38e3@pws-pc.ntlworld.com> In-Reply-To: <6DD493A4-22A1-4DB9-9400-5B73559C6B0E@gmail.com> References: <6DD493A4-22A1-4DB9-9400-5B73559C6B0E@gmail.com> X-Mailer: Claws Mail 3.8.0 (GTK+ 2.24.7; x86_64-redhat-linux-gnu) Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable On Sat, 1 Nov 2014 15:31:21 +0100 Eliseo Mart=C3=ADnez wrote: > To me, the following is not logical/expected behaviour: >=20 > $ echo $SHLVL > 1 > $ zsh -c 'echo $SHLVL' > 2 > $ zsh -c '(echo $SHLVL)' > 1 >=20 > I=E2=80=99d just like to know if it is a bug, or has some kind of explana= tion. I think the explanation is it's a bug. There's a complication owing to the shell knowing it doesn't actually need to fork, but it certainly looks like one weirdness too far to expose to the user. In a bit more detail: there are optimisations in the shell so that it knows it doesn't need to fork a new shell when it's about to exit the parent shell anway. A quick reading of POSIX (2.12, Shell Execution Environment, which describes a subshell environment at the end) suggests this is OK --- it's not tied to what actual process you're in, just how it affects the environment seen by the command and the parent shell, the last being academic in this case as you're about to exit. When it doesn't fork, it assumes that means it's about to start some command that now takes over the function of the current shell. But that's not true here --- we're actually going to be running code within the current shell but treating it as a subshell environment. So currently we don't set the "forked" variable in execcmd() in this case. But the other uses of this variable are mostly to do with noting that if we haven't forked, we need to be able to restore the current shell environment after the command, which is also irrelevant here. So as far as I can see, we can ensure what the user sees is sane by setting the "forked" flag for an explicit subshell environment even if we haven't forked. Decrementing SHLVL to reflect an exec when we're not forked --- i.e. if we're starting a new shell we note that we're not one level deeper because in this case the new shell replaced the old one --- was a bit controversial when it was introduced. It's the right thing to do if SHLVL is trying to answer the question "how many levels of shell are over my head right now in terms of processes", wrong if SHLVL is trying to answer the question "how many levels of shell execution did I go through before arriving at this point". It's possible setting forked =3D 1 other times when we set is_exec is the right thing to do. pws diff --git a/Src/exec.c b/Src/exec.c index 5bbd4e1..d2d4e80 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2995,6 +2995,15 @@ execcmd(Estate state, int input, int output, int how= , int last1) * Note that any form of exec means that the subshell is fake * * (but we may be in a subshell already). */ is_exec =3D 1; + /* + * If we are in a subshell environment anyway, say we're forked, + * even if we're actually not forked because we know the + * subshell is exiting. This ensures SHLVL reflects the current + * shell, and also optimises out any save/restore we'd need to + * do if we were returning to the main shell. + */ + if (type =3D=3D WC_SUBSH) + forked =3D 1; } =20 if ((esglob =3D !(cflags & BINF_NOGLOB)) && args && htok) { diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index d7f39cb..0cbe6c9 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1548,7 +1548,7 @@ foo=3D print ${foo:wq} print ${:wq} -0:Empty parameter shouldn't cause modifiers to crash the shell +0:Empty parameter should not cause modifiers to crash the shell > > =20 @@ -1656,3 +1656,10 @@ >h:i >j,k >l + + SHLVL=3D1 + $ZTST_testdir/../Src/zsh -c 'echo $SHLVL' + $ZTST_testdir/../Src/zsh -c '(echo $SHLVL)' +0:SHLVL appears sensible when about to exit shell +>2 +>2