4

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
1
  • @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 ^^ Commented Aug 17, 2015 at 15:03

4 Answers 4

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
1
  • 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… Commented Aug 17, 2015 at 15:26
1

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.

2
  • 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) Commented Aug 17, 2015 at 14:16
  • Why wouldn't ${(f)"$(cat "${a[@]}")"} generate an array of 200 elements if the first file in a has 100 lines and the second file in a has 100 lines?
    – user172877
    Commented Oct 29, 2024 at 15:23
0

You could do:

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

But that's not very legible.

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[@]".

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.