3

suppose you have an array a=(foo 'bar baz')

is there a more obvious way to apply a command/function to every array element and save the resulting strings into another array than this:

b=()
for e in $a; do
    b+=("$(my_cmd "$e")")
done
  • @jimmij: funny that you lowercased the title: I wanted to submit it like that at first, but StackExchange told me “The title does not fullfill quality requirements and grammar”, so I changed it ^^ – flying sheep Aug 17 '15 at 15:03
3

You can always declare a function for that:

map() {
  local arrayname="$1" cmd="$2" i
  shift 2
  eval "$arrayname=()"
  for i do
    eval "$arrayname+=(\"\$($cmd)\")"
  done
}

And use as:

$ a=(a '' bcd)
$ map b 'wc -c <<< "$i"' "$a[@]"
$ echo $b
2 1 4
  • if there’s no better builtin, defining my own probably the ideal option. although a signature like map dest-name source-name cmd and args..., as well as xargs-like behavior (append as last argument or use {} as placeholder) would be better than supplying the command as string and using a magic $i variable… – flying sheep Aug 17 '15 at 15:26
0

You could do:

eval "b=(" '"$(cmd "$a['{1..$#a}']")"' ")"

But that's not very legible.

0

If you know in advance what the command is doing with array elements then you can use parameter expansion flags (see man zshexpn) to instruct zsh how to adapt command output to array.


Example

Let's say an array a contains the names of files, and each file contains only one line of text:

$ a=(foo 'bar baz')
$ cat foo
A
$ cat 'bar baz'
B C

Now let's assume for argument sake, that the command is just cat to put into an array b the content of files. In this case instead of loop we can simply write

b=(${(f)"$(cat "${a[@]}")"})

where (f) splits the result of cat into separates words at newlines.

The result is as expected:

$ echo "${b[1]}"
A
$ echo "${b[2]}"
B C

According to the real case scenario you can split on different characters with flag ps:<character>:. Some of them as newline (ps:\n:) from the example above or NULL character (ps:\0:) have shorthand: (f) and (0) respectively.

  • A note about that: ${(ps:\0:)"$(cmd)"} will split on sequences of NULs (if cmd outputs a\0\0b\0, that will split into 'a' and 'b'). You can use "${(@ps:\0:)"$(cmd)"}" but then that will split into 'a', '', 'b' and '' which is not what you want either unfortunately if you want to process NUL delimited (as opposed to separated) records (also note that command substitution strips all trailing newline characters) – Stéphane Chazelas Aug 17 '15 at 14:16
0

Just a note:

you can make it shorter with:

for e ($a) b+=("$(my_cmd "$e")")

Note that $a loops over the non-empty elements. To loop over all the elements, it's "$a[@]".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.