5

I would like to instantiate a zsh array from a subshell

myarray=($(somecommand)

and I check if I received what I wanted with

for element in $myarray ; do echo "===" ; echo $element ; echo "---" ; done

By default I see that whitespaces (spaces and line breaks) are used to separate elements.

I also found that I can use ${(s:-:)"$(somecommand)"} to separate by -, in which case spaces and line breaks do not break elements (i.e. array elements can contain line breaks).

So far I fail to split at line breaks only. I.e. if somecommand returns

Alice
Bob
Arthur C Clarke
Neo
Thomas Anderson

I want my above for loop to print:

===
Alice
---
===
Bob
---
===
Arthur C Clarke
---
===
Neo
---
===
Thomas Anderson
---

How do I achieve that? (And possibly pointers where to look that up in the manual.)

13

The native splitting operator (beside the Bourne-like $IFS word splitting which is done on unquoted command substitutions) is with the s parameter expansion flag:

array=(${(ps:\n:)"$(cmd)"})

would split the output of cmd on newline, discarding the empty elements (the empty lines).

p is to enable those \x expansions. As ps:\n: is a common one, it's got a shorter alias: f (to split on line feeds):

array=(${(f)"$(cmd)"})

To preserve the empty lines, you'd do:

array=("${(f@)$(cmd)"})

Now, beware that like in most other shells, command substitution strips all the trailing newline characters, so all trailing empty lines. To preserve those, you can do:

array=("${(f@)$(cmd; echo .)}")
array[-1]=()  # remove that last line added by echo .

With $IFS word splitting:

IFS=$'\n'
array=($(cmd)) # removes empty lines. Note that contrary to other
               # Bourne-like shells, zsh doesn't do globbing there
               # so you don't need the "set -o noglob"

IFS=$'\n\n' # like in ksh93, doubling an IFS-whitespace character
            # removes its special treatment as a whitespace character
array=($(cmd)) # preserves empty lines except the trailing ones

IFS=$'\n\n'
array=($(cmd; echo .)); array[-1]=() # preserves all empty lines.

To avoid modifying $IFS globally, you can do the above in an anonymous function:

(){
  local IFS=$'\n\n'
  array=($(cmd; echo .)); array[-1]=()
}

Also beware that the $array expansion skips the empty elements. So if you wanted to loop over all the elements including the empty ones, you'd need:

for i ("$array[@]") ...

or

for i ("${(@)array}") ...

not

for i ($array) ...
for i in $array

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.