Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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

array_of_lines=("${(@f)$(my_command)}");

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?

share|improve this question
    
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" –  user815423426 Jan 22 '12 at 23:52
3  
@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

3 Answers 3

up vote 20 down vote accepted

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

array_of_lines=("${(@f)$(my_command)}")

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

share|improve this answer

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
c
d
e f
% print $arr[1]
a b

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

share|improve this answer

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'`
IFS="$vt"
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.

share|improve this answer
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

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.