8

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.

muru
  • 72,889
HappyFace
  • 1,612
  • Why do you want to return an array? Save the values in an array, and then use the array after the function is called. How is the function being used? – muru Aug 12 '19 at 06:57
  • @muru I use this function to turn existent paths in mpv's args to absolute paths, like this mpv() command mpv --sub-auto=fuzzy --fs --input-ipc-server "$mpv_ipc" "${(0@)$(rpargs "$@")}" – HappyFace Aug 12 '19 at 06:58

2 Answers2

8

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.

6

The idiomatic way is to just use the reply array. If you want, you can use the (P) flag and return in an array whose name is passed to the function:

myfunc() {
  local arrayname=${1:-reply}; shift
  # ...
  : ${(PA)arrayname::="${args[@]}"}
}
psprint
  • 153
  • What's the A flag and ::=? – HappyFace Apr 26 '20 at 00:36
  • ::= is a force assignment (as opposed to ${name=word} where name is changed only if it was unset.) A converts the substitution into an array --- it's otherwise interpreted as a string. Stephane Chazelas's solution seems clearer to me; is there an advantage to this? – Michaël Nov 27 '20 at 19:20