Learn by reading through in order

Reading Input — Handling a File Line by Line with while read

This article is part of the Linux Course, where you master practical Linux skills from scratch, from basic commands through to shell scripting.
Process data.txt line by line and split it into columns with while read name age, handle multiple files passed as arguments in order with for f in "$@", and feed read input via pipes or here-docs — illustrated and practiced in a browser terminal.

Reading a file line by line — while read

You often want to process a roster or config file one line at a time.

When you write while read line; do … done < file, it reads file from the top one line at a time, puts that line into the variable line, and repeats the body between do … done.

No matter how many lines the file has, it loops automatically all the way to the last line.

read splits the line it receives on whitespace and can put the pieces into several variables.

When you write while read name age; do …, the first word of each line goes into name and the next word into age.

For data laid out as "name value" like a roster, you can pull out each column and reformat it.

printf 'web running\ndb stopped\n' > services.txt   # create the sample file
while read svc state; do                              # read each line into svc and state
  echo "Service $svc is $state"
done < services.txt                                   # feed services.txt as input
# output: Service web is running / Service db is stopped
Processing a file line by line with while read
data.txtwhile read name agename = aliceage = 30one line at a time1st word2nd wordrepeat until lines run out
while read name age reads a file one line at a time and, splitting on whitespace, puts the first word into name and the next into age.
SyntaxMeaningExample
while read v; do … done < fProcess file f one line at a time with variable vwhile read l; do echo "$l"; done < data.txt
while read a b; do …Split a line on whitespace into several variableswhile read name age; do echo "$name"; done < data.txt

Create a script that reads a roster file one line at a time with while read and reformats it into the shape "name is age years old".

① Run printf 'alice 30\nbob 25\ncarol 41\n' > data.txt to create a sample file laid out as "name age".

② Open roster.sh with vi roster.sh, press i to enter insert mode, and write a script that reads each line with while read name age and prints "$name is $age years old" with echo (put #!/bin/sh on the first line). When done, press Esc:wq to save.

③ Add execute permission with chmod +x roster.sh, run ./roster.sh, and confirm the three lines come out reformatted.

④ If you are unsure what to write, you can copy the body from the answer panel and paste it into vi's insert mode.

(Run it correctly and the explanation will appear.)

Linux console
0 / 4 completed
Loading Linux Terminal...

Receiving multiple files as arguments — "$@" and $(cat f)

If you hardcode the filename to process inside the script, you can't use it on another file.

When you pass them at run time like ./show.sh a.txt b.txt, $1 becomes the first and $2 the second argument.

You can receive all the arguments together with "$@".

When you write for f in "$@"; do … done, you can put each passed file into f one at a time and process them in order.

When you want to use the whole file as a single string, pull it into a variable with text=$(cat "$f").

You get the number of arguments with $#, so you can make it show usage when there are 0.

printf 'line A1\nline A2\n' > a.txt              # sample file 1
printf 'line B1\n' > b.txt                       # sample file 2
vi show.sh   # open in the editor, write the following, and save with :wq
#   #!/bin/sh
#   for f in "$@"; do
#     echo "--- $f ---"
#     cat "$f"
#   done
chmod +x show.sh
./show.sh a.txt b.txt                            # show 2 files in order with headers
Processing multiple files passed as arguments in order
./view.sh a.txt b.txt$1 = a.txt$2 = b.txtfor f in "$@" in order1st2ndreceive all with "$@", in order
The run-time files go into $1 and $2, and with "$@" you receive them together and process them one at a time with for.
SyntaxMeaningExample
for f in "$@"; do …Process each argument file in orderfor f in "$@"; do cat "$f"; done
$(cat f)Pull the whole file into a single stringtext=$(cat data.txt)
$#The number of arguments (show usage if 0)[ "$#" -eq 0 ] && echo usage

Create a script that processes the received filenames one at a time as arguments and shows each header and contents in order.

① Run printf 'apple\nbanana\n' > fruit.txt and printf 'red\nblue\n' > color.txt to create two sample files.

② Open view.sh with vi view.sh, press i to enter insert mode, and write a script that receives the arguments one at a time with for f in "$@", prints a header with echo, then pulls the whole file into a variable and shows its contents (put #!/bin/sh on the first line). When done, press Esc:wq to save.

③ Add execute permission with chmod +x view.sh, run ./view.sh fruit.txt color.txt, and confirm the two files are shown in order.

④ If you are unsure what to write, you can copy the body from the answer panel and paste it into vi's insert mode.

Linux console
0 / 5 completed
Loading Linux Terminal...

Interactive input and pipe input — read and here-doc

read can read not only files but also input typed by a person or passed through a pipe.

There are forms like connecting with a pipe as in echo hi | read v, and feeding input on the spot all at once with a <<'EOF' … EOF here-doc.

This is the basis for combining your script with other commands.

# read a pipe's output one line at a time
printf 'one\ntwo\n' | while read v; do echo "line: $v"; done
# feed input on the spot with a here-doc
while read v; do echo "value: $v"; done <<'EOF'
x
y
EOF
# read manual input once (type name and press Enter)
# running this waits for input
read name; echo "Hello $name"
Feeding read with input without a file
printf 'cat\ndog'| while read vprocess line by line<<'EOF' … EOFwhile read vprocess line by linefeed with a pipefeed with a here-doc
Even without a file, you can feed while read input one line at a time with a pipe or a here-doc.
SyntaxMeaningExample
cmd | while read v; do …Process a pipe's output one line at a timecat data.txt | while read l; do echo "$l"; done
<<'EOF' … EOFFeed input on the spot with a here-docwhile read l; do echo "$l"; done <<'EOF'

Create a script that feeds while read input with a pipe and a here-doc, without going through a file.

① Open pipe.sh with vi pipe.sh, press i to enter insert mode, and write a script that pipes the output of printf 'cat\ndog\n' into while read v; do echo "Animal: $v"; done (put #!/bin/sh on the first line). When done, press Esc:wq to save.

② Add execute permission with chmod +x pipe.sh, run ./pipe.sh, and confirm two lines come out.

③ Confirm the output is Animal: cat and Animal: dog, showing you can process input one line at a time without creating a file.

④ If you are unsure what to write, you can copy the body from the answer panel and paste it into vi's insert mode.

Linux console
0 / 3 completed
Loading Linux Terminal...
QUIZ

Knowledge Check

Answer each question one by one.

Q1How does while read line; do … done < file process file?

Q2Which notation represents all the arguments passed to the script together?

Q3How do you process a command's output one line at a time with read, without creating a file?