I thought the following would group the output of my_command in an array of lines:

IFS='\n' array_of_lines=$(my_command);

so that $array_of_lines[1] would refer to the first line in the output of my_command, $array_of_lines[2] to the second, and so forth.

However, the command above doesn't seem to work well. It seems to also split the output of my_command around the character n, as I have checked with print -l $array_of_lines, which I believe prints elements of an array line by line. I have also checked this with:

echo $array_of_lines[1]
echo $array_of_lines[2]

In a second attempt, I thought adding eval could help:

IFS='\n' array_of_lines=$(eval my_command);

but I got the exact same result as without it.

Finally, following the answer on List elements with spaces in zsh, I have also tried using parameter expansion flags instead of IFS to tell zsh how to split the input and collect the elements into an array, i.e.:


But I still got the same result (splitting happening on n)

With this, I have the following questions:

Q1. What are "the proper" ways of collecting the output of a command in an array of lines?

Q2. How can I specify IFS to split on newlines only?

Q3. If I use parameter expansion flags as in my third attempt above (i.e. using @f) to specify the splitting, does zsh ignore the value of IFS? Why didn't it work above?


3 Answers 3




First mistake (→ Q2): IFS='\n' sets IFS to the two characters \ and n. To set IFS to a newline, use IFS=$'\n'.

Second mistake: to set a variable to an array value, you need parentheses around the elements: array_of_lines=(foo bar).

This would work, except that it strips empty lines, because consecutive whitespace counts as a single separator:

IFS=$'\n' array_of_lines=($(my_command))

You can retain the empty lines except at the very end by doubling the whitespace character in IFS:

IFS=$'\n\n' array_of_lines=($(my_command))

To keep trailing empty lines as well, you'd have to add something to the command's output, because this happens in the command substitution itself, not from parsing it.

IFS=$'\n\n' array_of_lines=($(my_command; echo .)); unset 'array_of_lines[-1]'

(assuming the output of my_command doesn't end in a non-delimited line; also note that you lose the exit status of my_command)

Note that all the snippets above leave IFS with its non-default value, so they may mess up subsequent code. To keep the setting of IFS local, put the whole thing into a function where you declare IFS local (here also taking care of preserving the command's exit status):

collect_lines() {
  local IFS=$'\n\n' ret
  array_of_lines=($("$@"; ret=$?; echo .; exit $ret))
  unset 'array_of_lines[-1]'
  return $ret
collect_lines my_command

But I recommend not to mess with IFS; instead, use the f expansion flag to split on newlines (→ Q1):


Or to preserve trailing empty lines:

array_of_lines=("${(@f)$(my_command; echo .)}")
unset 'array_of_lines[-1]'

The value of IFS doesn't matter there. I suspect that you used a command that splits on IFS to print $array_of_lines in your tests (→ Q3).

  • 9
    This is so complicated! "${(@f)...}" is the same as ${(f)"..."}, but in a different way. (@) inside double quotes means “yield one word per array element” and (f) means “split into an array by newline”. PS: Please link to the docs Aug 17, 2015 at 11:11
  • 6
    @flyingsheep, no ${(f)"..."} would skip empty lines, "${(@f)...}" preserves them. That's the same distinction between $argv and "$argv[@]". That "$@" thing to preserve all elements of an array comes from the Bourne shell in the late 70s. Aug 13, 2019 at 12:28
  • "You can retain the empty lines except at the very end by doubling the whitespace character in IFS:" I wouldn't expect that to work, since IFS is itself a set of characters, not a string. Is that a special case? I couldn't get it to work in my testing; double newlines in IFS still split on every line.
    – Mark Reed
    21 hours ago
  • 1
    @MarkReed That's a zsh thing: in IFS, “If an IFS white space character appears twice consecutively in the IFS, this character is treated as if it were not an IFS white space character”. With IFS=$'\n', separators are sequences of newlines, so information about empty lines (consecutive newlines) disappears and the array only has the non-empty lines. With IFS=$'\n', splitting happens at each newline, so there is a separate array element for each line, empty or not, apart from trailing empty lines which $(…) removes. 18 hours ago
  • Ah, I misunderstood the mechanism and thought you were somehow splitting on double-newlines. Got it now, thanks!
    – Mark Reed
    18 hours ago

Two issues: first, apparently double quotes also don't interpret backslash escapes (sorry about that :). Use $'...' quotes. And according to man zshparam, to collect words in an array you need to enclose them in parenthesis. So this works:

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
e f
% print $arr[1]
a b

I can't answer your Q3. I hope I'll never have to know such esoteric things :).


You can also use tr to replace newline with space:

lines=($(mycommand | tr '\n' ' '))
select line in ("${lines[@]}"); do
  echo "$line"
  • 3
    what if the lines contain spaces ? Oct 21, 2016 at 16:51
  • 2
    That makes no sense. Both SPC and NL are in the default value of $IFS. Translating one to the other makes no difference. Aug 13, 2019 at 12:29
  • Were my edits reasonable? I couldn't get it to work as it was,
    – John P
    Aug 13, 2019 at 18:11
  • (timed out) I admit I edited it without really understanding what the intent was, but I think the translation is a good start for separation based on string manipulation. I wouldn't translate to spaces, and split on them, unless the expected behavior were more like echo, where inputs are all more or less heaps of words separated by who cares.
    – John P
    Aug 13, 2019 at 18:25

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.