Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Differences between "TRAPERR()" and "trap foo ERR"

--------------------------------- BACKGROUND ---------------------------------
I've discovered some undocumented behavior of TRAPNAL functions, and
some divergence of behavior between TRAPNAL and "trap foo NAL".

Specifically, the former receives a $1 with the value of the signal
which invoked the routine, but the latter does not -- and that value
is the actual value of the signal (e.g. $1 == SIGSEGV == 11 for

For example, TRAPERR receives a value of 32, which is not a valid
signal value on my system (NSIG==32).

When setting via "trap 'my_trapfunc $@' SEGV", no argument is provided
to 'my_trapfunc'.

All of this is on ZSH v5.8.

---------------------------------- EXAMPLES ----------------------------------
So I've got two scripts, traperr and trapfunc.

    $ cat traperr
    #!/usr/bin/env zsh
    TRAPERR() {
        echo "Got ${(q-)@}"

    $ cat trapfunc
    #!/usr/bin/env zsh
    trap_err() {
        echo "Got ${(q-)@}"
    trap trap_err ERR

Note that "traperr" declares an all-caps TRAPERR.  "trapfunc" instead
uses the "trap" command to set the ERR trap.

Both generally work as expected, but the former ("TRAPERR") receives
an argument in "$1" that is undocumented.

    $ ./traperr
    Got 32

    $ ./trapfunc

------------------------------- DOCUMENTATION --------------------------------
I looked at the manual page (section 9.3.1) for trap functions:

However, there's nothing mentioned about any parameters being
specified to TRAPNAL functions, and it generally seems that "TRAPNAL"
and "trap foo NAL" behave the same.

------------------------------ EXPERIMENTATION -------------------------------
At first, I thought the provenance of the value 32 comes from the
value of NSIG on my system (which on macOS is 32).

    $ cat nsig.c
    #include <stdio.h>
    #include <signal.h>

    int main() {
        printf("NSIG = %d\n", NSIG);

    $ gcc -o nsig nsig.c

    $ ./nsig
    NSIG = 32

Second, I thought that perhaps it might come from ZSH's own list of
signals.  There is a ZERR entry, in any case.

    $ echo $signals

    $ for i in {0..${#signals}}; do echo "$i: ${signals[$i]}"; done
    1: EXIT
    2: HUP
    31: USR1
    32: USR2
    33: ZERR
    34: DEBUG

As we can see here, the value received by "33: ZERR", is not the 32'th
entry in the list of $signals.  Since ZSH array indices start at 1,
this makes more sense and getting "32" for ZERR, is correct.

This is confirmed by setting e.g. TRAPSEGV, where SIGSEGV==11.

    $ cat trapsegv
    #!/usr/bin/env zsh
    TRAPSEGV() {
        echo "Got ${(q-)@}"
    kill -s SEGV $$

    $ ./trapsegv
    Got 11

So we are receiving the actual value of the signal, when we declare a
TRAPNAL function.  This corresponds to the NAL+1'th offset in the
$signals array.

--------------------- ATTEMPT TO CONVERGE FUNCTIONALITY ----------------------
Since arguments are being provided to TRAPNAL functions, I thought I
would try to modify the "trapfunc" script to include its arguments
when invoking trap_err (note the extra $@ inside the trap statement
versus before).

    $ cat trapfunc_args
    #!/usr/bin/env zsh
    trap_err() {
        echo "Got ${(q-)@}"
    trap 'trap_err $@' ERR

    $ ./trapfunc_args

It seems this does not work to specify the signal number by including
e.g. $@ in the trap command list.

------------------------ CONCLUSION AND OBSERVATIONS -------------------------
Overall, there are two observations.

1. The behavior that TRAPNAL will receive NAL as its first argument,
where NAL corresponds to a the system-defined signal values (e.g.
SIGSEGV == 11).  The name for this signal can be gotten from
${signals[x+1]}.  This should be documented.

2. This behavior does not expand to "trap func NAL" statements, even
if we try to pass "$@" to some function.  This seems like a bug.

Am I missing something in the docs (quite likely) -- and is the
behavior difference intended -- or are these bugs.

Thanks for reading!
Zach Riggle

Messages sorted by: Reverse Date, Date, Thread, Author