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,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 26361 invoked from network); 4 Jan 2021 00:23:25 -0000 Received: from zero.zsh.org (2a02:898:31:0:48:4558:7a:7368) by inbox.vuxu.org with ESMTPUTF8; 4 Jan 2021 00:23:25 -0000 ARC-Seal: i=1; cv=none; a=rsa-sha256; d=zsh.org; s=rsa-20200801; t=1609719805; b=g2m3RqSNnoCI4hXaVBF4JPzCl5uePhdeum05Sac5XH8m2Lq9lrRr97FWqZf31vPymYXc4c28O/ cNySrkX7TDvWTC+53qiz/hpVZ67oLxVd6z541w5vtQ9GSdoryLG/AtTw5ZVETqEOoMt/LWm9Fj xa15zchkr4J/v2Vzwjq9U7YZcF6JmPjf7EWQdGWObG1KNgF7Rho1e5X3C91M5yWYPOW0Nbo2U1 7K7F59zpCFTXHvRjQrVsV8A7Yhd7cgi/SJtNd68CPU3u/tWvQr5jYzYu5fjOXX26qhsq9ftZpQ faxeGaQcl+7yjXU/68PIZozh2hma2P74CBrovePetGltpA==; ARC-Authentication-Results: i=1; zsh.org; iprev=pass (injection.crustytoothpaste.net) smtp.remote-ip=192.241.140.119; dkim=pass header.d=crustytoothpaste.net header.s=default header.a=rsa-sha256; dmarc=none header.from=crustytoothpaste.net; arc=none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed; d=zsh.org; s=rsa-20200801; t=1609719805; bh=FQ24iJMFxpBGrNB9ZQoCT8gPQddbcoFiqCgDOdViDUM=; h=List-Archive:List-Owner:List-Post:List-Unsubscribe:List-Subscribe:List-Help: List-Id:Sender:Content-Transfer-Encoding:Content-Type:MIME-Version: References:In-Reply-To:Message-ID:Date:Subject:To:From:DKIM-Signature: DKIM-Signature; b=k6he8OYGVv5nn17FeYDxJ+eBD73rPhpaUrNLpfdNpg8FK1b8i5vqSVQw0KgBYg8reO7zTKbupE HypdJhjDqZmwgCESdQqXIyg4rEbqY4NVo5jsQAhyzAQ02yAujR9qEHToNqmzyMDlWEx5D2lJ1y z5Ko7Pr3pWtcqmaCkfk6+TmrWHqA+e3hEHMGcGPGwAL5+Qyc+uzm0aW6TleEtkyF+jcvMsoqYL PmWs4F/uuVeU9lr3UNppVNGjYAa+1xmRZd5+aj5e4pbO6tOQYIA7nYmQV/lcWKbz3cHeyok2Jb 20YVJNuJzUQvBfMHES1qz80AvzUJk8/S44s4/DM0yEN43g==; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=zsh.org; s=rsa-20200801; h=List-Archive:List-Owner:List-Post:List-Unsubscribe: List-Subscribe:List-Help:List-Id:Sender:Content-Transfer-Encoding: Content-Type:MIME-Version:References:In-Reply-To:Message-Id:Date:Subject:To: From:Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID; bh=knLIriTs3t49FeuiVLhiOVI2LDBZ4rp7nm8XaIWPx40=; b=xx/+OL0a9WpJ0lmIGf4Ux0JrLI yGQTC2ulHyDi5N55+4w6fOn8Oh2hwxYfRqaKoXplkW1zwYaDy5dhOi2IXKhnbpnGRI4hhpLUesNxy tjAnviJBn2ofSp//9HI4tJO81xtXJAS5NEHAQI8D4G+q/LLRcKrIWSUpfJ9dBLOChGeCYf0DThhmx pRh29SnK8rSr+lgJZzx7vMuSj8bVMr8HKYRyFtHMODg+GQxln1GazCWPjXhQhHlbasxluhvyEUYbU 929WrKumiMZV8JF4mdHu1piSSwavDLRNfLXwg0yy61jZBEgGEedyqa3kf+T6suDeR1Op7TNh11E6d 2gxH04VQ==; Received: from authenticated user by zero.zsh.org with local id 1kwDee-000Ob7-RL; Mon, 04 Jan 2021 00:23:24 +0000 Authentication-Results: zsh.org; iprev=pass (injection.crustytoothpaste.net) smtp.remote-ip=192.241.140.119; dkim=pass header.d=crustytoothpaste.net header.s=default header.a=rsa-sha256; dmarc=none header.from=crustytoothpaste.net; arc=none Received: from injection.crustytoothpaste.net ([192.241.140.119]:47964) by zero.zsh.org with esmtps (TLS1.2:ECDHE-RSA-CHACHA20-POLY1305:256) id 1kwDeA-000OCK-73; Mon, 04 Jan 2021 00:22:55 +0000 Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b978:101:b610:a2f0:36c1:12e3]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by injection.crustytoothpaste.net (Postfix) with ESMTPSA id 666F360811 for ; Mon, 4 Jan 2021 00:22:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1609719772; bh=FQ24iJMFxpBGrNB9ZQoCT8gPQddbcoFiqCgDOdViDUM=; h=From:To:Subject:Date:In-Reply-To:References:Content-Type:From: Reply-To:Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To: Resent-Cc:In-Reply-To:References:Content-Type:Content-Disposition; b=ppz8JuAyAOmX4bqhBHMiMBfax6FxYUH6thkWOZBzBv33ag0g/e9syr37zH8eEruG8 /zNmJmi0N451KU+7OuNlgbB33oOK+sia733/mRpgi0tOy+dUX+/FYAyoCv9yjXBOHq PdOE32arje9CrD65nR3j6wWGC8von/hdZHlpiAqsc4b/FkUa5OVvW8qWi6jz0Ix/GQ jq5oRJ89PWJKBT+RU8KsofRcI50DF5WhAiK/gv05QXzpLb9NdBX6bS9xwcxIX2d+IV AUjCLA/nfWZK9Fp/IdLwb6NGfVZJu9dX7Qtds97YSLww88q8Wx+pA14B/Hy4H58rMH xsx525f43Pvnu0Wy2f9JfKKmJNIUK6krMqN4er5h9O7IHpAVKKrM8WccSfD4CLvwMg g65LZs2z5+ysADCuQvh/u1ocQd2kg9uQzccy0j57Gf/rLeGwVck8LZcgZxNYLpUJRz cbpprJY76TAjb0QNjsZa5QmIxv2Hb2AdIoa3Q8kKt35YtwQ004O From: "brian m. carlson" To: zsh-workers@zsh.org Subject: [PATCH v3 1/1] exec: run final pipeline command in a subshell in sh mode Date: Mon, 4 Jan 2021 00:22:13 +0000 Message-Id: <20210104002213.2703305-2-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.30.0.284.gd98b1dd5eaa7 In-Reply-To: <20210104002213.2703305-1-sandals@crustytoothpaste.net> References: <20210104002213.2703305-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Seq: 47794 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: Archived-At: zsh typically runs the final command in a pipeline in the main shell instead of a subshell. However, POSIX specifies that all commands in a pipeline run in a subshell, but permits zsh's behavior as an extension. The default /bin/sh implementations on various Linux distros and the BSDs always use a subshell for all components of a pipeline. Since zsh may be used as /bin/sh in some cases (such as macOS Catalina), it makes sense to have the common sh behavior when emulating sh, so do that by checking for being the final item of a multi-item pipeline and creating a subshell in that case. >From the comment above execpline(), we know the following: last1 is a flag that this command is the last command in a shell that is about to exit, so we can exec instead of forking. It gets passed all the way down to execcmd() which actually makes the decision. A 0 is always passed if the command is not the last in the pipeline. […] If last1 is zero but the command is at the end of a pipeline, we pass 2 down to execcmd(). So there are three cases to consider in this code: • last1 is 0, which means we are not at the end of a pipeline, in which case we should not change behavior. • last1 is 1, which means we are effectively running in a subshell, because nothing that happens due to the exec is going to affect the actual shell, since it will have been replaced. So there is nothing to do here. • last1 is 2, which means our command is at the end of the pipeline, so in sh mode we should create a subshell by forking. input is nonzero if the input to this process is a pipe that we've opened. At the end of a multi-stage pipeline, it will necessarily be nonzero. Note that several of the tests may appear bizarre, since most developers do not place useless variable assignments directly at the end of a pipeline. However, as the function tests demonstrate, there are cases where assignments may occur when a shell function is used at the end of a command. The remaining assignment tests simply test additional cases, such as the use of local, that would otherwise be untested. --- README | 4 ++++ Src/exec.c | 10 ++++++---- Test/B07emulate.ztst | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/README b/README index 9b1b1605f..3877594ae 100644 --- a/README +++ b/README @@ -92,6 +92,10 @@ not set the new, fourth field will continue to work under both 5.8 and 5.9. (As it happens, adding a comma after "bold" will make both 5.8 and 5.9 do the right thing, but this should be viewed as an unsupported hack.) +emulate sh: When zsh emulates sh, the final command in a pipeline is now run in +a subshell. This differs from the behavior in the native (zsh) mode, but is +consistent with most other sh implementations. + Incompatibilities between 5.7.1 and 5.8 --------------------------------------- diff --git a/Src/exec.c b/Src/exec.c index ecad923de..0f6f48b23 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2882,11 +2882,13 @@ execcmd_exec(Estate state, Execcmd_params eparams, pushnode(args, dupstring("fg")); } - if ((how & Z_ASYNC) || output) { + if ((how & Z_ASYNC) || output || + (last1 == 2 && input && EMULATION(EMULATE_SH))) { /* - * If running in the background, or not the last command in a - * pipeline, we don't need any of the rest of this function to - * affect the state in the main shell, so fork immediately. + * If running in the background, not the last command in a + * pipeline, or the last command in a multi-stage pipeline + * in sh mode, we don't need any of the rest of this function + * to affect the state in the main shell, so fork immediately. * * In other cases we may need to process the command line * a bit further before we make the decision. diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst index 7b1592fa9..45c39b51d 100644 --- a/Test/B07emulate.ztst +++ b/Test/B07emulate.ztst @@ -276,3 +276,25 @@ F:Some reserved tokens are handled in alias expansion 0:--emulate followed by other options >yes >no + + emulate sh -c ' + foo () { + VAR=foo && + echo $VAR | bar && + echo "$VAR" + } + bar () { + tr f b && + VAR="$(echo bar | tr r z)" && + echo "$VAR" + } + foo + ' + emulate sh -c 'func() { echo | local def="abc"; echo $def;}; func' + emulate sh -c 'abc="def"; echo | abc="ghi"; echo $abc' +0:emulate sh uses subshell for last pipe entry +>boo +>baz +>foo +> +>def