As @donjuedo statet, space is usually regarded as a separator, that's why you don't get the whole lines. There are several ways to work around this.
I list the solutions for both reading from file and from the output of a command. As input you can create a file with the following content and name it testfile.txt (with empty lines, lines with spaces in between and also on both ends:
This is the first line
2
third line
fifth line
sixth line
Solution 1: most generally applicable
while IFS= read -u 3 -r line; do
echo ">${line}<"
read -p "press enter to show next line" var
echo "read caught the following input: >$var<"
done 3< testfile.txt
The variant that reads from a pipe looks exactly the same, only the last line changes. As an example I process all lines from the testfile that contain spaces, which eliminates lines two and four (here with <&3 instead of -u 3 - both are equivalent):
while IFS= read -r line <&3; do
...
done 3< <(grep " " testfile.txt)
This works for large input files. The 3< indirection seems awkward, but makes sure, that processes within the loop still can read from standard input (see the read statement "press enter..."). This might be important if you execute commands that might show user prompts themselfes (like rm etc).
So:
works for large input files as well without blowing up memory
stdin is not redirected (instead fd 3 is used)
spaces inbetween, and at both ends of lies are retained (using IFS)
empty lines are retained es well
Thanks to @BenjaminW for proposing this.
Solution 2: with IFS and for (with restrictions)
OFS="$IFS"
IFS=$'\n'
for line in $(cat testfile.txt) ; do
echo ">${line}<"
read -p "press enter to show next line" var
echo "read caught the following input: >$var<"
done
IFS="$OFS"
This temporarily changes the field separator to newline, so the file is only splitted on line breaks.
not recommended for large input because of the command line substitution ($(cat testfile))
stdin is also not redirected, so it is possible to use stdin in the body without restricitons
spaces inbetween, and at both ends of lies are retained (using IFS)
empty lines as line four are skipped here (line legth 0 / matching ^$)
if you use this, you have to make sure, that you reset IFS
it might get messy, if your loop body needs a different interpretation of fields (e.g. needs to read something else which should be split around spaces).