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
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
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
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
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.
${(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
${(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?
You could do:
eval "b=(" '"$(cmd "$a['{1..$#a}']")"' ")"
But that's not very legible.
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[@]"
.