Ksh Read a File Line By Line ( UNIX Scripting )

Posted on in Categories , , , , , , last updated August 23, 2015

How do I read a file line by line using KSH shell scripting under UNIX like operating systems?

You can use the while loop and read command to read a text file line by line under KSH.

KSH read while loop syntax

# while loop
while IFS= read -r line
        # display line or do somthing on $line
	echo "$line"
done <"$file"

In this example, you are reading file separated by | fields. Sample domains.txt:

# set the Internal Field Separator to a pipe symbol
# file name
# use while loop to read domain and ip 
while read domain ip
	print "$domain has address $ip"
done <"$file"

However, following is recommend syntax to set the Internal field separator (see discussion below):

# file name
# use while loop to read domain and ip 
# set the Internal Field Separator to a pipe symbol
while IFS=\| read domain ip
	print "$domain has address $ip"
done <"$file"

Suggested readings:

  • ksh man page

Updated for accuracy!

Posted by: Vivek Gite

The author is the creator of nixCraft and a seasoned sysadmin and a trainer for the Linux operating system/Unix shell scripting. He has worked with global clients and in various industries, including IT, education, defense and space research, and the nonprofit sector. Follow him on Twitter, Facebook, Google+.

18 comment

  1. Thanks, I usually don’t like doing my heavy lifting with shell scripting. Still, this is useful information because sometimes the shell is all you have in the environment you’re working in.


  2. Thanks for the scripting tips, they really are the key to effective system administration tasks. The differences between the shells is new to me, I usually just use bash since it’s the default on the systems I use. I looked up the Korn shell on Wikipedia and it reports that it has backwards compatibility to the Bourne shell (which is Bash’s predecessor). On my system I did an experiment to see if the scripts would work with Bash and changed the #!/bin/ksh to #!/bin/sh. The first one works find, the second doesn’t – looks like it doesn’t support the “IFS” environment label. Interesting.

    Now, if someone can explain the different between this alphabet soup and sym links that would be helpful (Ubuntu 8.04.1). Never heard of ‘dash’!

    -rwxr-xr-x 1 root root 702160 May 12 2008 /bin/bash
    -rwxr-xr-x 1 root root 79988 Mar 12 2008 /bin/dash
    lrwxrwxrwx 1 root root 21 Nov 20 14:05 /bin/ksh -> /etc/alternatives/ksh
    lrwxrwxrwx 1 root root 4 Apr 24 2008 /bin/sh -> dash

  3. When there are a few lines to be read, I do always use the following method.

    while read line
    echo “$line”
    done << iplist

  4. @Joel,

    The original sh is not available under Linux. dash is the standard command interpreter for the system. The current version of dash is in the process of being changed to conform with the POSIX 1003.2 and 1003.2a specifications for the shell. This version has many features which make it appear similar in some respects to the Korn shell, but it is not a Korn shell clone.

    /etc/alternatives/ksh should point to to ksh93. Run the following

    ls -l /etc/alternatives/ksh

    sym links allows to use different version of ksh without breaking anything. update-alternatives command maintain symbolic links determining default commands for various system commands, and java. See man page

    man ksh
    man dash
    man update-alternatives
  5. The safe way to change IFS in a ‘while read’ loop is:

    while IFS=\| read a b c
    : whatever

    Then it doesn’t affect anything but the read command.

  6. To Chris : short and simple – How nice!
    Should have think of this good old shell syntax
    Good example of KISS principle … (ask Wikipedia)

  7. while reading line by line like this if you want to take user desire input
    eg :-
    cat use |while read mo
    read -r o
    if [[ $o = ‘y’ ]]
    print “$mo”
    print “kill me”

    In this case o/p will be always kill me …….user input wouldn’t be there so …..how to take user input in that

    1. @ishan

      Solution is to separate user input from data input.
      Use Vivek’s tutorial:

      A solution may be:

      # File descriptor #0 is stdin
      # File descriptor #1 is stdout
      # File descriptor #2 is stderr
      # Let us assign extra file descriptor to file for input
      # open for input file named "data" with fd #3 
      exec 3< data
      # loop on fd #3
      while read <&3
      	# ask user input, by default on stdin
      	read -p "Print next line of data ? (y/n):" answer 
      	# to debug, uncomment the following line
      	# echo "Answer:${answer}:"
      	# branch on answer, casted to lowercase
      	if [[ "${answer,,}" = 'y' ]]
      		# user wants to see data 
      		# which is by default read in builtin REPLY variable
      		echo $REPLY
      		# user wants to quit 
      		# let us terminate while loop
      # Close fd #3
      exec 3<&-

      HTH, Try it and let us know your solution.

      — Philippe

  8. Dear Experts,
    I have a scenario where in
    we need read file (.csv) row by row and
    Select the value for the column “Q2014”.
    which further segregated into (Q1 2014 ,Q2 2014,Q3 2014,Q4 2014) we need to get the Sum Q1 2014 for all products.
    If the Sum(Q1 2014 ) > sum(Q2 2014) if the difference is > 5% then send a mail.
    Your help in highly appreciable.

    Thanks in Advance,

    1. Prashanth,
      This is probably better handled by an application than a Korn shell script, but it sounds like an interesting challenge, so here goes.
      First, it isn’t clear to me from your question how your .csv file is formatted, but I’m going to assume the following:

      Q1 2013,Q2 2013,Q3 2013,Q4 2013,Q1 2014,Q2 2014

      If your CSV file has a different format, you should be able to modify the solution below to match it.

      let linenum=1
      let col1=-1
      let col2=-1
      let sum1=0
      let sum2=0
      while read fline
        set -A farray $fline
        let numcols=${#farray[*]}
        if [[ $linenum -eq 1 ]]; then
          for (( x=1 ; $x <= $numcols ; x+=1 ))
            if [[ $farray[x] == "Q1 2014" ]]; then
              let col1=$x
            if [[ $farray[x] == "Q2 2014" ]]; then
              let col2=$x
          if [[ $col1 -eq -1 || $col2 -eq -1 ]]; then
            echo 'One or both column headers not found'
            exit 1
          let sum1+=${farray[$col1]}
          let sum2+=${farray[$col2]}
        let linenum+=1
      done < "datafile.csv"
      # (I'm assuming sum1 is the basis for the 5%)
      let delta=$sum1-$sum2
      let delta*=20
      if [[ $delta -ge $sum1 ]]; then
        echo 'Send an email notification'

      Obviously, the echo statement would be replaced by whatever you use to send an email from the script. I've also hardcoded the filename and column header strings for simplicity, but I assume you'd make those values script inputs.
      This works for me, but I might have missed something (I'm not a scripting expert by any means) so comments/corrections from experts are welcomed.

      Edited by mods

  9. could any one please tell me how to declare sql statement inside ‘do’ and get output in a text


  10. Dear Experts,
    I have a scenario where in
    we need read file (.csv) row by row and
    And print the column header with its value
    i.e input.csv
    n1 , n2, n3
    then we want to print this in text file as
    Q1 n1^]Q2 n2^]Q3 n3

    Your help in highly appreciable.

    Thanks in Advance,

  11. Hi Guys,

    I’m having a scenario.
    1. UNIX script needs to pick a sample.schema file and a source.txt file(fixed length file i.e no delimeter)
    2. Based on the schema file it should split the source file data into columns in a csv format file.
    3. Finally source file data should be viewed in 3 columns with respect to schema file.

    sample.schema (record length =54):-

    {final_delim=none, delim=none, quote=none}

    Source.txt (Record length of each record is 54) :
    RG0013001056704RH1001 BLADENSBURG, KEETON A
    RG0013001431587RH1001 HOSTETTER, ASHLYN M
    RG0013001484327RH1001 BRETHREN, KENNY K

  12. I am trying to get filesystem /mysqlshare in all my servers ie i have 300 servers trying to get details of all.But for now trying get only 2 server details from txt file ie(text.txt) which have
    when i try this script its only reading 1st server details and getting exit from script

    # while loop
    while IFS= read -r a
            # display line or do somthing on $line
            output=`ssh $a df -h | grep mysqlshare`
    #echo $a,$output
    echo $a ,$output >>/home/mysqladm/server_list/output.txt
    done <"$file"

    Can someone help me out..

Comments are closed.