1
#!/bin/bash

get_data() {
  echo foo
  echo "bar baz"
}

for i in $(get_data); do
   echo "got: $i"
done

actual output is

got: foo
got: bar
got: baz

what I want is

got: foo
got: bar baz

How do I get the variable i in for i in $(...) to be filled per-line?

spraff
  • 32,570
  • 22
  • 121
  • 229
  • Does this answer your question? [How do I split a string on a delimiter in Bash?](https://stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash) – qmeeus Jan 05 '23 at 10:30
  • 3
    `for i in $(..)` is a broken construct; it is subject to **word-splitting** (which you can somehow manage by setting `IFS`) and worse, to **glob expansion** – Fravadona Jan 05 '23 at 10:36
  • 1
    See also [don't read lines with `for`](http://mywiki.wooledge.org/DontReadLinesWithFor) – tripleee Jan 05 '23 at 10:59

1 Answers1

2

Here is how I would do it:

get_data | while IFS= read -r i ; do
  echo "got $i"
done

The reason why you are not getting what you want with for is that $(get_data) gets expanded by bash to foo bar baz; by default, new lines are treated as word boundaries like space and nothing more. Similarly, using for to loop over contents of a file (for line in $(cat file)) will not work as expected.

P.S. You could modify the IFS (field separator) like this:

IFS=$'\n'

as suggested in one of the comments; however I prefer my solution as more explicit and less error-prone.

January
  • 16,320
  • 6
  • 52
  • 74
  • Don't use a `while`-loop in a pipeline as it is executed in a sub-shell. Changes to, or definition of variables will not be transferred to the parent. Just use `while IFS= read -r line; do cmd "$line"; done < <(get_data)`. (See [BashFAQ#024](https://mywiki.wooledge.org/BashFAQ/024)) – kvantour Jan 05 '23 at 11:11
  • 2
    The `read` command should have been `IFS= read -r i`. `IFS=` is in order to preserve leading and trailing blanks. `-r` option is in order to prevent backslashes from being an escape character. – M. Nejat Aydin Jan 05 '23 at 11:12
  • @M.NejatAydin forgot about the leading and trailing space. Updated my comment. Thanks. – kvantour Jan 05 '23 at 11:13
  • @kvantour: I actually prefer the subshell solution. I find this `while ... done < < ...` idiom hard to read and confusing. Is there any other reason to favor it apart of having local variables? – January Jan 05 '23 at 20:58