0

In my file 'foo' there is a first line: 'a b', and the second line: 'c d':

$ cat foo
a b 
c d

I want to print in terminal in loop these two lines: one after another:

$ for i in $(cat foo); do echo $i; done

But in the output 'echo' command breaks the order, so instead of having:

a b
c d

I actually have:

a
b
c
d
Raja G
  • 102,391
  • 106
  • 255
  • 328
Josef Klimuk
  • 1,596

2 Answers2

4

for i in $(cat foo) does not loop lines but words (or fields) split by your field separator $IFS. It default to \n\t (space, newline, tab).

If you change your field separator to newline only, you can use your command as is (Disclaimer: please read below!):

IFS=$'\n'
for i in $(cat foo); do echo $i; done

You might want to make a backup of IFS to restore it later:

OLD_IFS="$IFS"
.
.
.
IFS="$OLD_IFS"

Output:

a b
c d

However, this is considered bad practice.

  • Bit Better: while IFS= read -r line; do ... done < file
  • Much better: Use a dedicated tool for your task such as grep, sed or awk.

Please read Why is using a shell loop to process text considered bad practice?.

pLumo
  • 26,947
2

You can use while read for this purpose

[/tmp]$ while read line ; do echo $line ; done < foo                                     
a b
c d

Adding a separator for your understanding

[/tmp]$ while read line ; do echo $line ; echo "----" ; done < foo                       
a b
----
c d
----
[/tmp]$    
Raja G
  • 102,391
  • 106
  • 255
  • 328
  • If I want to go with your solution, but also to add a conditional statement inside my for loop, then what will be "$i"? I mean, something like this: for i in $(while read line ; do echo $line ; done < foo); do if [ grep -c a "$i" -gt 1 ]; then echo "$i";fi;done – Josef Klimuk Dec 08 '21 at 12:20
  • 1
    Note that $line should be quoted "$line" for similar reasons (otherwise it will be word split into separate tokens - try with a b in place of a b for example). – steeldriver Dec 08 '21 at 12:26
  • 3
    ... as well, read would still remove any leading whitespace and expand escape sequences by default, so more generally if you want to echo lines exactly you'd want while IFS= read -r line; do ... or (to include a possibly improperly terminated last line) while IFS= read -r line || [[ -n "$line" ]]; do ... – steeldriver Dec 08 '21 at 12:35
  • 1
    @JosefKlimuk while read -r; do if ! grep -oe 'a' <<< "$REPLY" | sed -n 2q1; then echo "$REPLY"; fi; done < text, -c only count matched lines, and since you only have one line it will never be more than 1. I like to use sed to count occurrence as it can return an exit status. –  Dec 08 '21 at 13:49