Read line by line in bash script
xargs
is the most flexible solution for splitting output into command arguments.
It is also very human readable and easy to use due to its simple parameterisation.
Format is xargs -n $NUMLINES mycommand
.
For example, to echo
each individual line in a file /tmp/tmp.txt
you'd do:
cat /tmp/tmp.txt | xargs -n 1 echo
Or to diff
each successive pair of files listed as lines in a file of the above name you'd do:
cat /tmp/tmp.txt | xargs -n 2 diff
The -n 2
instructs xargs
to consume and pass as separate arguments two lines of what you've piped into it at a time.
You can tailor xargs
to split on delimiters besides carriage return/newline.
Use man xargs
and google to find out more about the power of this versatile utility.
The best way to do this is to redirect the file into the loop:
# Basic idea. Keep reading for improvements.
FILE=test
while read CMD; do
echo "$CMD"
done < "$FILE"
A redirection with < "$FILE"
has a few advantages over cat "$FILE" | while ...
. It avoids a useless use of cat, saving an unnecessary child process. It also avoids a common pitfall where the loop runs in a subshell. In Bash, commands in a |
pipeline run in subshells, which means variable assignments are lost after the loop ends. Redirection with <
doesn't have that problem, so you could use $CMD
after the loop or modify other variables inside the loop. It also, again, avoids unnecessary child processes.
There are some additional improvements that could be made:
- Add
IFS=
so thatread
won't trim leading and trailing whitespace from each line. - Add
-r
toread
to prevent backslashes from being interpreted as escape sequences. - Lower-case
CMD
andFILE
. The Bash convention is that only environmental and internal shell variables are uppercase. - Use
printf
in place ofecho
which is safer if$cmd
is a string like-n
, whichecho
would interpret as a flag.
file=test
while IFS= read -r cmd; do
printf '%s\n' "$cmd"
done < "$file"
What you have is piping the text "cat test"
into the loop.
You just want:
cat test | \
while read CMD; do
echo $CMD
done