Requested by ajacoutot@, here's an attempt at implementing set -o pipefail. As pointed by sthen@ this option should be included in the next POSIX standard:
https://www.austingroupbugs.net/view.php?id=789 There are several ways to implement it, the diff I showed to Antoine was based on option 2) in https://www.austingroupbugs.net/view.php?id=789#c4102 whereas the diff below implements option 1) described by kre@NetBSD, which looks like the most sensible approach. No, this doesn't add support for a PIPESTATUS array, I'm not sure we need it. This diff includes manpage and regress bits. Thoughts, oks? Index: bin/ksh/jobs.c =================================================================== RCS file: /d/cvs/src/bin/ksh/jobs.c,v retrieving revision 1.61 diff -u -p -r1.61 jobs.c --- bin/ksh/jobs.c 28 Jun 2019 13:34:59 -0000 1.61 +++ bin/ksh/jobs.c 6 Jul 2020 18:10:43 -0000 @@ -70,6 +70,7 @@ struct proc { #define JF_REMOVE 0x200 /* flagged for removal (j_jobs()/j_notify()) */ #define JF_USETTYMODE 0x400 /* tty mode saved if process exits normally */ #define JF_SAVEDTTYPGRP 0x800 /* j->saved_ttypgrp is valid */ +#define JF_PIPEFAIL 0x1000 /* pipefail on when job was started */ typedef struct job Job; struct job { @@ -421,6 +422,8 @@ exchild(struct op *t, int flags, volatil */ j->flags = (flags & XXCOM) ? JF_XXCOM : ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE)); + if (Flag(FPIPEFAIL)) + j->flags |= JF_PIPEFAIL; timerclear(&j->usrtime); timerclear(&j->systime); j->state = PRUNNING; @@ -1084,7 +1087,30 @@ j_waitj(Job *j, j_usrtime = j->usrtime; j_systime = j->systime; - rv = j->status; + + if (j->flags & JF_PIPEFAIL) { + Proc *p; + int status; + + rv = 0; + for (p = j->proc_list; p != NULL; p = p->next) { + switch (p->state) { + case PEXITED: + status = WEXITSTATUS(p->status); + break; + case PSIGNALLED: + status = 128 + WTERMSIG(p->status); + break; + default: + status = 0; + break; + } + if (status) + rv = status; + } + } else + rv = j->status; + if (!(flags & JW_ASYNCNOTIFY) && (!Flag(FMONITOR) || j->state != PSTOPPED)) { Index: bin/ksh/ksh.1 =================================================================== RCS file: /d/cvs/src/bin/ksh/ksh.1,v retrieving revision 1.208 diff -u -p -r1.208 ksh.1 --- bin/ksh/ksh.1 26 Nov 2019 22:49:01 -0000 1.208 +++ bin/ksh/ksh.1 6 Jul 2020 16:04:34 -0000 @@ -361,7 +361,9 @@ token to form pipelines, in which the st last is piped (see .Xr pipe 2 ) to the standard input of the following command. -The exit status of a pipeline is that of its last command. +The exit status of a pipeline is that of its last command, unless the +.Ic pipefail +option is set. A pipeline may be prefixed by the .Ql \&! reserved word, which causes the exit status of the pipeline to be logically @@ -3664,6 +3666,10 @@ See the and .Ic pwd commands above for more details. +.It Ic pipefail +The exit status of a pipeline is the exit status of the rightmost +command in the pipeline that doesn't return 0, or 0 if all commands +returned a 0 exit status. .It Ic posix Enable POSIX mode. See Index: bin/ksh/misc.c =================================================================== RCS file: /d/cvs/src/bin/ksh/misc.c,v retrieving revision 1.73 diff -u -p -r1.73 misc.c --- bin/ksh/misc.c 28 Jun 2019 13:34:59 -0000 1.73 +++ bin/ksh/misc.c 6 Jul 2020 16:04:34 -0000 @@ -147,6 +147,7 @@ const struct option sh_options[] = { { "notify", 'b', OF_ANY }, { "nounset", 'u', OF_ANY }, { "physical", 0, OF_ANY }, /* non-standard */ + { "pipefail", 0, OF_ANY }, /* non-standard */ { "posix", 0, OF_ANY }, /* non-standard */ { "privileged", 'p', OF_ANY }, { "restricted", 'r', OF_CMDLINE }, Index: bin/ksh/sh.h =================================================================== RCS file: /d/cvs/src/bin/ksh/sh.h,v retrieving revision 1.75 diff -u -p -r1.75 sh.h --- bin/ksh/sh.h 20 Feb 2019 23:59:17 -0000 1.75 +++ bin/ksh/sh.h 6 Jul 2020 16:13:59 -0000 @@ -158,6 +158,7 @@ enum sh_flag { FNOTIFY, /* -b: asynchronous job completion notification */ FNOUNSET, /* -u: using an unset var is an error */ FPHYSICAL, /* -o physical: don't do logical cd's/pwd's */ + FPIPEFAIL, /* -o pipefail: all commands in pipeline can affect $? */ FPOSIX, /* -o posix: be posixly correct */ FPRIVILEGED, /* -p: use suid_profile */ FRESTRICTED, /* -r: restricted shell */ Index: regress/bin/ksh/obsd-regress.t =================================================================== RCS file: /d/cvs/src/regress/bin/ksh/obsd-regress.t,v retrieving revision 1.11 diff -u -p -r1.11 obsd-regress.t --- regress/bin/ksh/obsd-regress.t 22 May 2020 08:45:23 -0000 1.11 +++ regress/bin/ksh/obsd-regress.t 6 Jul 2020 20:09:34 -0000 @@ -514,3 +514,91 @@ description: stdin: kill -s SIGINFO $$ --- + +name: pipeline-pipefail-1 +description: + check pipeline return status +stdin: + set -o pipefail + true | true +--- + +name: pipeline-pipefail-2 +description: + check pipeline return status +stdin: + set -o pipefail + false | true +expected-exit: e == 1 +--- + +name: pipeline-pipefail-3 +description: + check pipeline return status +stdin: + set -o pipefail + true | false +expected-exit: e == 1 +--- + +name: pipeline-pipefail-4 +description: + check pipeline return status +stdin: + set -o pipefail + ! false | true +--- + +name: pipeline-pipefail-errexit-1 +description: + check pipeline return status +stdin: + set -e + false | true + echo "ok" +expected-stdout: ok +--- + +name: pipeline-pipefail-errexit-2 +description: + check pipeline return status +stdin: + set -e + set -o pipefail + false | true + echo "should not print" +expected-exit: e == 1 +expected-stdout: +--- + +name: pipeline-pipefail-errexit-3 +description: + check pipeline return status +stdin: + set -e + set -o pipefail + false | true || echo "ok" +expected-stdout: ok +--- + +name: pipeline-pipefail-check-time-1 +description: + check pipeline return status +stdin: + false | true & + p=$! + set -o pipefail + wait $p +--- + +name: pipeline-pipefail-check-time-2 +description: + check pipeline return status +stdin: + set -o pipefail + false | true & + p=$! + set +o pipefail + wait $p +expected-exit: e == 1 +--- -- jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF DDCC 0DFA 74AE 1524 E7EE
