While loop with input redirection and read
command.
You should not be using cut
to perform a sequential iteration of each line in a file as cut
was not designed to do this.
Print selected parts of lines from each FILE to standard output.
— man cut
TL;DR
You should use a while loop with the read -r
command and redirect standard input to your file inside a function scope where IFS
is set to \n
and use -E
when using echo
.
processFile() { # Function scope to prevent overwriting IFS globally
file="$1" # Any file that exists
local IFS="\n" # Allows spaces and tabs
while read -r line; do # Read exits with 1 when done; -r allows \
echo -E "$line" # -E allows printing of \ instead of gibberish
done < $file # Input redirection allows us to read file from stdin
}
processFile /path/to/file
Iteration
In order to iterate over each line of a file, we can use a while loop. This will let us iterate as many times as we need to.
while <condition>; do
<body>
done
Getting our file ready to read
We can use the read
command to store a single line from standard input in a variable. Before we can use that to read a line from our file, we need to redirect standard input to point to our file. We can do this with input redirection. According to the man pages for bash, the syntax for redirection is [fd]<file
where fd
defaults to standard input (a.k.a file descriptor 0
). We can place this before or after our while loop.
while <condition>; do
<body>
done < /path/to/file
# or the non-traditional way
</path/to/file while <condition>; do
<body>
done
Reading the file and ending the loop
Now that our file can be read from standard input, we can use read
. The syntax for read
in our context is read [-r] var...
where -r
preserves the \
(backslash) character, instead of using it as an escape sequence character, and var
is the name of the variable to store the input in. You can have multiple variables to store pieces of the input in but we only need one to read an entire line. Along with this, to preserve any backslashes in any output from echo
you will likely need to use the -E
flag to disable the interpretation of backslash escapes. If you have any indentation (spaces or tabs), you will need to temporarily change the IFS
(Input Field Separators) variable to only "\n"
; normally it is set to " \t\n"
.
main() {
local IFS="\n"
read -r line
echo -E "$line"
}
main
How do we use read
to end our while loop?
There is really only one reliable way, that I know of, to determine when you've finished reading a file with read
: check the exit value of read
. If the exit value of read
is 0
then we successfully read a line, if it is 1
or higher then we reached EOF (end of file). With that in mind, we can place the call to read
in our while loop's condition section.
processFile() {
# Could be any file you want hardcoded or dynamic
file="$1"
local IFS="\n"
while read -r line; do
# Process line here
echo -E "$line"
done < $file
}
processFile /path/to/file1
processFile /path/to/file2
A visual breakdown of the above code via Explain Shell.