6

I have a variable:

❯ echo $numholds     
409503
409929
409930
409932
409934
409936
409941
409942
409944
409946

I want to do a for loop of it, but the newline-delimitation doesn't work:

❯ for num in $numholds; do
echo $num ,
echo New Item!
> done                         
409503
409929
409930
409932
409934
409936
409941
409942
409944
409946 ,
New Item!

This even happens when I set IFS=$"\n \t". How can I make this work?

2 Answers 2

12

To split on newline in zsh, you use the f parameter expansion flag (f for line feed) which is short for ps:\n::

for num (${(f)numholds}) print -r New item: $num

You could also use $IFS-splitting which in zsh (contrary to other Bourne-like shells) you have to ask for explicitly for parameter expansion using the $=param syntax ($= looks a bit like a pair of scissors):

for num ($=numholds) print -r New item: $num
4

Your variable is a string, not an array. That it happens to contain newlines isn't relevant. Your for num in $numholds sees the entire thing as a single value since that's how you have it stored.

The simple workaround would be to use a while loop and the <<< here-string construct to feed it:

% while read num; do echo "$num"; echo "New Item"; done <<<"$numholds"
409503
New Item
409929
New Item
409930
New Item
409932
New Item
409934
New Item
409936
New Item
409941
New Item
409942
New Item
409944
New Item
409946
New Item

This is essentially equivalent to the uglier (see here for why this is a bad idea):

% for num in $(echo "$numholds"); do echo $num; echo "new item"; done
409503
new item
409929
new item
409930
new item
409932
new item
409934
new item
409936
new item
409941
new item
409942
new item
409944
new item
409946
new item

Finally, you might want to make the variable an array instead:

numArray=($(echo "$numholds"))

You can now use the construct you wanted:

% for num in "${numArray[@]}"; do echo $num; echo "new item"; done                   
409503
new item
409929
new item
409930
new item
409932
new item
409934
new item
409936
new item
409941
new item
409942
new item
409944
new item
409946
new item
tpad% 
2
  • That's definitely not how you'd do it in zsh. Yes $IFS-splitting is done by default upon parameter expansion, but running a separate command in a subshell and get its output over a pipe just for that (or store variable in a temp file and read it one byte at at time with the <<< approach) is overkill and inefficient when you could invoke the splitting operators instead. Feb 15, 2020 at 21:28
  • 1
    @StéphaneChazelas ah, good, I was hoping you'd notice this. I'm sure you're right, I don't use zsh and I was certain that you or one of the other zshHeads would give a better solution. I just did what I'd do in bash and tested on zsh to see it works.
    – terdon on strike
    Feb 15, 2020 at 21:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .