Zsh Scripting – Idiomatic Way of Returning an Array in a Zsh Function

arrayfunctionzsh

I have this function,

rpargs () {
    local i
    args=()
    for i in "$@"
    do
        test -e "$i" && args+="$(realpath --canonicalize-existing -- "$i")"  || args+="$i"
    done
}

And I want to return args. The only ways I can think of are either to printf '%s\0' and then split it via expansion flags (0@), or to use a global like the code above.

Best Answer

zsh's return builtin can only return a 32bit signed integer like the _exit() system call. While that's better than most other Bourne-like shells, that still can't return arbitrary strings or list of strings like the rc/es shells. The return status is more about returning a success/failure indication.

Here, alternatively, you can have the function take the name of the array to fill in as argument, like:

myfunc() {
  local arrayname=$1; shift
  # ...
  eval $arrayname'=("$elements[@]")'
  # the returned $? will be 0 here for success unless that eval command
  # fails.
}

myfunc myarray other args

Your printf '%s\0' approach wouldn't work for array elements that contain NULs.

Instead you could use the qq parameter expansion flag to quote elements on output, and the z (to parse quotes) and Q (to remove quoting) on input like:

myfunc() {
   # ...
   print -r -- ${(qq)elements}
}

myarray=("${(@Q)${(z)$(myfunc)}}")

But in addition to being less legible, it's also less efficient as it means forking a process and transfering the output of myfunc through a pipe in addition to the quoting/unquoting.