I'm working on a script (that will run on OSX, and probably nothing else), that basically just parses the /var/log/accountpolicy.log* logs for authentication times/counts. The initial command is a zgrep executed via sudo, which is piped to awk, executing an awk script. After the command is ran, I use ${PIPESTATUS[@]} to determine if anything failed, and if so, which part.

Here's the awk script in its current state:

#! /usr/local/bin/awk -f

  return_code = 0
  if ( length( username ) == 0 ){
    return_code = 2
    exit return_code

  rows = 0
  if ( $8 != sprintf("\"%s\",", username ) ) next

  rows = rows+1
  print $0
  if ( return_code > 0 ) exit return_code
  if ( rows == 0 ) exit 3

The awk script has some custom value validation and exit codes. The return codes 1, 2 and 3 mean:

  1. Awk failed (for some reason related to awk)
  2. No username was specified for awk to parse for
  3. The username was specified, but no values were found

Test #1 (working properly)

An example execution (hiding the output from the awk script, since this question is related to return codes specifically):

$ sudo -n zgrep -h AuthenticationAllowed /var/log/accountpolicy.log* 2>/dev/null | awk -v username="${USER}" -f ./parse-accountpoliocy.awk &>/dev/null
$ echo ${PIPESTATUS[@]}
0 0

You can see that ${PIPESTATUS[@]} shows that both sudo and awk were successful, which is expected, as I know I have sudo access, and the awk variable username was set and has log entries.

Test #2 (working properly)

Now if we change the awk variable username to an account that does not exist, then the awk script should exit with a return code of 3:

$ sudo -n zgrep -h AuthenticationAllowed /var/log/accountpolicy.log* 2>/dev/null | awk -v username="fakeuser" -f ./parse-accountpoliocy.awk &>/dev/null
$ echo ${PIPESTATUS[@]}
0 3


Test #3 (Problem..)

If I execute the command above, but neglect to define the username awk variable, then the awk script should exit with a return code of 2

$ sudo -n zgrep -h AuthenticationAllowed /var/log/accountpolicy.log* 2>/dev/null | awk -f ./parse-accountpoliocy.awk &>/dev/null
$ echo ${PIPESTATUS[@]}
141 2

As you can see, the awk script does return 2, but now for some reason, the sudo/zgrep returns 141, despite the fact that that part of the command wasn't altered at all... and this is the problem I’m running into.

I even tried to execute the command without hiding the output (of either STDOUT or STDERR), and the result was the same, but no error was displayed:

$ sudo -n zgrep -h AuthenticationAllowed /var/log/accountpolicy.log*  | awk -f ./parse-accountpoliocy.awk
$ echo ${PIPESTATUS[@]}
141 2


How can the exit code of the awk script change the exit code for the sudo/zgrep command stored within ${PIPESTATUS[@]}?

Environmental Information

  • OSx Version: 10.11.6 (El Capitan)
  • Bash Version: 4.4.12
  • AWK Version: GNU Awk 4.1.4
  • As an aside, because you already have your SIGPIPE answer, you don't need your rows variable as awk already has the NR variable bultin which counts the records (lines in this case).
    – icarus
    Commented Dec 26, 2017 at 18:32

1 Answer 1

$ echo $((141-128))
$ kill -l | grep 13
13   PIPE Broken pipe                   29   INFO Information request

So that 141 is how the shell kluges the 16-bit exit status word (see wait(2)) containing a PIPE signal into a single number; this does not happen for the exit 3 case as that happens after the data being piped has been done processed by awk (and zgrep has nothing more to write to awk via the pipe). The exit 2 instead happens very early while zgrep still has data it wants to pipe to awk, so zgrep gets whacked with a PIPE when it tries to write to the awk that has gone away.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .