≡ Menu

Linux/UNIX: Bash Read a File Line By Line

How do I read a file line by line under a Linux or UNIX-like system using KSH or BASH shell?

You can use while..do..done bash loop to read file line by line on a Linux, OSX, *BSD, or Unix-like system.

Syntax to read file line by line on a Bash Unix & Linux shell:

Tutorial details
DifficultyEasy (rss)
Root privilegesNo
RequirementsBash/ksh/zsh/tcsh
Estimated completion time10m
  1. The syntax is as follows for bash, ksh, zsh, and all other shells -
  2. while read -r line; do COMMAND; done < input.file
  3. The -r option passed to red command prevents backslash escapes from being interpreted.
  4. Add IFS= option before read command to prevent leading/trailing whitespace from being trimmed -
  5. while IFS= read -r line; do COMMAND_on $line; done < input.file

Here is more human readable syntax for you:

#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r var
do
  echo "$var"
done < "$input"
 

Examples

Here are some examples:

#!/bin/ksh
file="/home/vivek/data.txt"
while IFS= read line
do
        # display $line or do somthing with $line
	echo "$line"
done <"$file"

The same example using bash shell:

#!/bin/bash
file="/home/vivek/data.txt"
while IFS= read -r line
do
        # display $line or do somthing with $line
	printf '%s\n' "$line"
done <"$file"

You can also read field wise:

#!/bin/bash
file="/etc/passwd"
while IFS=: read -r f1 f2 f3 f4 f5 f6 f7
do
        # display fields using f1, f2,..,f7
        printf 'Username: %s, Shell: %s, Home Dir: %s\n' "$f1" "$f7" "$f6"
done <"$file"

Sample outputs:

Fig.01: Bash shell scripting-  read file line by line demo outputs

Fig.01: Bash shell scripting- read file line by line demo outputs

Bash Scripting: Read text file line-by-line to create pdf files

My input file is as follows (faq.txt):

4|http://www.cyberciti.biz/faq/mysql-user-creation/|Mysql User Creation: Setting Up a New MySQL User Account
4096|http://www.cyberciti.biz/faq/ksh-korn-shell/|What is UNIX / Linux Korn Shell?
4101|http://www.cyberciti.biz/faq/what-is-posix-shell/|What Is POSIX Shell?
17267|http://www.cyberciti.biz/faq/linux-check-battery-status/|Linux: Check Battery Status Command
17245|http://www.cyberciti.biz/faq/restarting-ntp-service-on-linux/|Linux Restart NTPD Service Command
17183|http://www.cyberciti.biz/faq/ubuntu-linux-determine-your-ip-address/|Ubuntu Linux: Determine Your IP Address
17172|http://www.cyberciti.biz/faq/determine-ip-address-of-linux-server/|HowTo: Determine an IP Address My Linux Server
16510|http://www.cyberciti.biz/faq/unix-linux-restart-php-service-command/|Linux / Unix: Restart PHP Service Command
8292|http://www.cyberciti.biz/faq/mounting-harddisks-in-freebsd-with-mount-command/|FreeBSD: Mount Hard Drive / Disk Command
8190|http://www.cyberciti.biz/faq/rebooting-solaris-unix-server/|Reboot a Solaris UNIX System

My bash script:

#!/bin/bash
# Usage: Create pdf files from input (wrapper script)
# Author: Vivek Gite <Www.cyberciti.biz> under GPL v2.x+
#---------------------------------------------------------
 
#Input file
_db="/tmp/wordpress/faq.txt"
 
#Output location
o="/var/www/prviate/pdf/faq"
 
_writer="~/bin/py/pdfwriter.py"
 
# If file exists 
if [[ -f "$_db" ]]
then
    # read it
	while IFS='|' read -r pdfid pdfurl pdftitle
    do
    	local pdf="$o/$pdfid.pdf"
        echo "Creating $pdf file ..."
	#Genrate pdf file
        $_writer --quiet --footer-spacing 2 \
        --footer-left "nixCraft is GIT UL++++ W+++ C++++ M+ e+++ d-" \
        --footer-right "Page [page] of [toPage]" --footer-line \
        --footer-font-size 7 --print-media-type "$pdfurl" "$pdf"
    done <"$_db"
fi
 

Tip: Read from bash variable

Let us say you want a list of all installed php packages on a Debian or Ubuntu Linux, enter:

# My input source is the contents of a variable called $list #
list=$(dpkg --list php\* | awk '/ii/{print $2}')
printf '%s\n' "$list"
 

Sample outputs:

php-pear
php5-cli
php5-common
php5-fpm
php5-gd
php5-json
php5-memcache
php5-mysql
php5-readline
php5-suhosin-extension

You can now read from $list and install the package:

#!/bin/bash
# BASH can iterate over $list variable using a "here string" #
while IFS= read -r pkg
do
    printf 'Installing php package %s...\n' "$pkg"
    /usr/bin/apt-get -qq install $pkg
done <<< "$list"
printf '*** Do not forget to run php5enmod and restart the server (httpd or php5-fpm) ***\n'
 

Sample outputs:

Installing php package php-pear...
Installing php package php5-cli...
Installing php package php5-common...
Installing php package php5-fpm...
Installing php package php5-gd...
Installing php package php5-json...
Installing php package php5-memcache...
Installing php package php5-mysql...
Installing php package php5-readline...
Installing php package php5-suhosin-extension...
*** Do not forget to run php5enmod and restart the server (httpd or php5-fpm) ***
Tweet itFacebook itGoogle+ itPDF itFound an error/typo on this page?

{ 17 comments… add one }

  • Chris F.A. Johnson February 8, 2011, 6:42 pm

    The bash example is correct for all shells. The ksh example is not correct; it will strip leading and trailing whitespace.

    To demonstrate, use:

    echo ":$line:"

    on a file with leading and/or trailing whitespace on one or more .

  • Anthony Thyssen September 14, 2011, 11:35 pm

    Reading a file line by line with a single loop is fine in 90% of the cases.

    But if things get more complicated then you may be better off opening the file as a file descriptor.

    Situations such as:

    * the read of the file is only a minor aspect and not the main task, but you want to avoid opening an closing the file multiple times. For example preserving where you have read up to or avoiding file closure (network, named pipes, co-processing)

    * the file being read requires different methods of parsing, that is better done in simpler but different loops. Reading emails (header and body) is an example of this, but so is reading files with separate operational and data blocks.

    * you are reading from multiple files at the same time, and switching between different files, in a pseudo-random way. That is you never quite know whch file you will need to read from next.

  • princess anu April 4, 2012, 7:01 pm

    I have a scenario like this. I have a file which has servername, port and ipaddress
    svr1
    port1
    ip1
    svr2
    port2
    ip2

    I tried couple of ways reading these records and assigning them a field and print the output to a file like below but nothing is helpful

    servername port ipaddress
    svr1 port1 ip1
    scr2 port2 ip2
    and so on

    can someone please help me.

    Thanks

  • princess anu April 5, 2012, 3:20 pm

    I figured it out. May be not professional but it is working for me.

    #!/bin/sh
    #!/bin/bash
    num=1
    echo -n "server name    "
    echo "port number"
    echo "-----------------------------------------"
    while read line
    do
    echo -n "$line        "
    i=$(($num % 2 ))
    if [ $i -eq 0 ]
    then
    echo "" #### print the new line ###
    fi
    num=`expr $num + 1`
    done< serverlist.txt
    

    Thanks

    • jl3128 August 25, 2015, 10:18 pm

      if the file is 100% predictable in each host,ip,port coming in groups of 3 you can use “paste”

      cat list.txt | paste – – –

  • Robert Edward Johnson June 10, 2012, 2:30 am

    HOW DO YOU SPLIT INPUT INTO FIELDS FOR PROCESSING?

    I have a question concerning how you’d actually process individual fields of input.
    For example, let’s say you’re doing the usual

    while read LINE; do
    process the statements
    done firstbyteinq and looking at firstbyteinq with a read, but 1). the cut doesn’t seem to be doing it just to the record being read, it does it to ALL the records all at one time; 2). the read of the firstbyteinq then advances the other file one record!!!

    It seems to me there ought to be some way to “declare” the positions from a to b as fields and THEN use the while read, but I’m not sure how to do that.
    I’ll try anything. Thanks VERY much in advance.

  • Chris F.A. Johnson June 10, 2012, 4:16 pm

    princess anu, there’s no need to use arithmetic; just read the file in groups of three lines:

    while :
    do
      read server
      read port
      read ipaddr
      : do whatever
    done < "$file"
    
  • Chris F.A. Johnson June 10, 2012, 4:28 pm

    A correction; you need to check that the read succeeded; checking the first should be enough:

    while :
    do
      read server || break
      read port
      read ipaddr
      : do whatever
    done < "$file"
    
  • MAYANK September 6, 2012, 8:19 am

    I have scenario :

    One file which contain values like:
    ABS
    SVS
    SGS
    SGS

    another file is template which has to use values from 1st file (One value at a time, line by line )

    ARGS=ABS >> save in one file

    New file:
    ARGS=SVS >> save in another file

    Can you please help me to achive this

    • bhupesh May 14, 2014, 6:30 am

      u can use paste command if the fields are same …..and then output the records to the other file and print another.

      • bhupesh May 14, 2014, 6:56 am

        rm 3 4 5
        first=1
        seco=2
        cat 1 | while read l
        do
        echo “$l =” >> 3
        done
        paste 3 2 | while read line
        do
        echo “$line” >> 4
        done
        awk ‘{print $1,$2,$3}’ 4 > 5

        now u can move the records line by line to another file

  • Name July 2, 2013, 1:35 pm

    echo “$line” # <— will fail if "$line" is "-n", it won't print anything at all, even an empty line.

    You should use printf builtin to work around this behavior:
    printf "%s\n" "$line"

  • Chris F.A. Johnson July 3, 2013, 11:40 am

    Some shells shells will output -n; that is what POSIX requires.

  • Steve January 28, 2014, 12:55 am

    My while/read is having an issue with the data not being printed for the last line read.
    The info is read and summed but is not being printed to the report.
    The script is reading a file with tab delimited fields, sorting on a field then reading each line while assigning the fields to var names.
    If statements determine whether the numbers are summed or if a new category is found with the previous categories data being written to the report. All categories except the last are complete in the report. The last category is left out.
    I added trouble shooting echo statements to check the workflow and they showed that the last category is being read and summed but it doesnt get printed.
    It’s as if the read gets to the last line, does the work and then doesnt know what to do with the output.
    The sorted data looks something like:

    247   an-IP   Name1
    37   another-IP   Name1
    52    more-IPs   Name2
    1562   etc   Name2
    29   etc   name3
    4   etc   name3
    The report has:
    284 Name1
    1591 Name2

    Code with troubleshooting echos and most comments deleted:

    #control vars
    GT=0
    name-Name
    cat  | sort -k3 | while read -r Count IP Name
    do
    # Handle the first line
       if [ "Name" != "$name" -a $GT -eq 0 ]
       then
          name=$Name
          GT=$Count
    # The read name is different than the last line (new category)
       elif [ "$Name != $name" -a $GT -gt 0 ]
       then
    # Write previous name and count to the report
          echo " ${name} - ${GT}" >>
    # Assign new categories values to the vars
          GT=$Count
          name=$Name
    # New line but same category found
       else [ "$Name" == "$name"  ]
    # Increment counts of the category
          GT=`expr $GT + $Count`
       fi
    done
    
  • name April 3, 2014, 9:45 am

    The read name is different than the last line (new category)

       elif [ "$Name != $name" -a $GT -gt 0 ]
       then
    # Write previous name and count to the report
          echo " ${name} - ${GT}" >>
    # Assign new categories values to the vars
          GT=$Count
          name=$Name
    # New line but same category found
       else [ "$Name" == "$name"  ]
    # Increment counts of the category
          GT=`expr $GT + $Count`
       fi
    done
    REPLY
    
  • Chris F.A. Johnson May 14, 2014, 11:25 am

    UUOC

    cat | sort -k3 | while read -r Count IP Name

    is the same as

    sort -k3 | while read -r Count IP Name

  • Stephane August 26, 2015, 6:47 am

    It is also possible to store the elements in a bash array using the -a option

    The following example reads some comma separated values and print them in reverse order:
    input.txt

       1,2,3,4
       A,B,C
    ==>
       4,3,2,1
       C,B,A
    
    Code
    
    while IFS="," read -a A ; do
       SEP=""
       for ((i=${#A[@]}-1;i>=0;i--)) ; do
         echo -n "$SEP${A[i]}"
         SEP=","
       done
       echo
    done < input.txt
    

Leave a Comment