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?

  • 1
    Just to let you know, single quotes don't interpret backslash escapes. Try with IFS="\n" ... (notice double quotes). – angus Jan 22 '12 at 20:16
  • Thanks @angus! Unfortunately I got the same result with "\n" – Amelio Vazquez-Reina Jan 22 '12 at 23:52
  • 4
    @angus Double quotes don't escape backslashes into control characters either. $'…' does. – Gilles Jan 23 '12 at 0:54
  • 1
    @Gilles: You're right, I noticed that later. Sorry again, intrpc. – angus Jan 23 '12 at 1:00

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:

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

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


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).

  • 6
    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 – flying sheep Aug 17 '15 at 11:11

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
  • 1
    what if the lines contain spaces ? – don_crissti Oct 21 '16 at 16:51

zsh drove me crazy when I tried to use it, so I stopped using it.

That said, here is how I would do what you're trying to do. zsh treats newline "specially" in IFS so you should avoid it. Substitute some other character in place of newline and IFS works mostly as you would expect. So if you make sure my_command never emits that character then you can use it as an ersatz line separator. In the example code below I use ASCII VT or control-K (octal 013) for this purpose.

vt=`echo foo | tr -s -c '\013' '\013'`
set -A array_of_lines $(my_command | tr '\012' '\013')
unset IFS

The first line puts the VT character into the vt variable. The second line sets IFS to only contain VT. The third line runs my_command and then converts all the output newlines into VTs with the tr command. set then breaks apart the output into separate parameters and stores them into array_of_lines. The fourth line resets IFS, which causes zsh to behave as if IFS were set to its default value.

Setting IFS and calling set on the same line like this

IFS="$vt" set -A array_of_lines $(my_command | tr '\012' '\013')

does not work. Don't ask me why; I hate zsh.

  • 2
    Your problem with zsh seems to be that you don't know it. I can't think of any special treatment of newlines in IFS, and your replacement with VT is a useless complication. The two basic problems in intrpc's code are the use of IFS='\n' when IFS=$'\n' was meant, and omitting parentheses in the definition of the array; these issues would be exactly the same in ksh or bash. – Gilles Jan 23 '12 at 0:53
  • 2
    Sorry, but when the same code works using VT but fails to work using newline, that is treating newline specially. I found zsh to be full of unexpected surprises, which I didn't need out of a shell. To each his own. – Kyle Jones Jan 23 '12 at 0:58

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.