Bash / KSH: Define Delimiter (IFS) While Using read Command

How do I can set IFS (internal field separator) while using read command in bash loops?

The IFS variable is used in as the input field separator. If you set IFS to | (i.e. IFS=| ), | will be treated as delimiters between words/fields when splitting a line of input. [donotprint]
Tutorial details
Difficulty level Easy
Root privileges No
Requirements None
Est. reading time 2m
[/donotprint]In the read command, IFS is used to split the line of input so that each variable gets a single field of the input. The default value is . You can print it with the following command:

cat -etv

Sample outputs:

 ^I$
$

In this example, read first and last name using read command and set IFS to a white space:

IFS=' ' read -p 'Enter your first and last name : ' first last 
echo "Hello, $first $last"

Sample outputs:

Enter your first and last name : Vivek Gite
Hello, Vivek Gite

In this example set IFS to | and read data:

IFS='|' read domain ip4 ip6 <<< 'google.com|74.125.236.65|2404:6800:4007:801::1008'
echo "$domain has $ip4 IPv4 and $ipv6 IPv6 address."

Sample outputs:

google.com has 74.125.236.65 IPv4 and  IPv6 address.

while loop example with IFS and read command

Create a text file (named foo.txt) as follows:
$ cat foo.txt
Sample outputs:

google.com|74.125.236.65|2404:6800:4007:801::1008
i.theos.in|58.27.86.81|2600:807:320:305::3f6e:f648
cyberciti.biz|75.126.153.206|2600:807:320:305::3f6e:f649

Create a bash shell script as follows:

#!/bin/bash
_input="foo.txt"
# set IFS (internal field separator) to |
# read file using while loop
while IFS='|' read -r domain ip4 ip6 
do
   echo "$domain has $ip4 IPv4 and $ip6 IPv6 address."
done < "$_input"

🐧 Get the latest tutorials on Linux, Open Source & DevOps via RSS feed or Weekly email newsletter.

🐧 7 comments so far... add one


CategoryList of Unix and Linux commands
Disk space analyzersdf duf ncdu pydf
File Managementcat cp mkdir tree
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Modern utilitiesbat exa
Network UtilitiesNetHogs dig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg glances gtop jobs killall kill pidof pstree pwdx time vtop
Searchingag grep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
7 comments… add one
  • Pierre B. Jul 25, 2012 @ 21:35

    Thx Vivek, i use custom IFS sometimes when i do bash scripts, but i’ve never had the idea to include it directly inside the while loop !
    That easy, quick, efficient and class, just what i like.

    Thx for the tips.

    Pierre B.

  • Pierre B. Jul 26, 2012 @ 8:15

    Hi Vivek,

    I did try to apply this trick to one of my script, but it seems that specifying the IFS this way broke something, when i put it back as it was before : it works again.

    Here is the function where i do use the custom IFS :

    Log_Chk() {
      # Usage: $0 called with the "log name" as $@ (as a list)
      #+ Note :only the "esmlog" is checked by this function, as it the only relevant log for hardware components status
      DOMAIN="ESMLOG"
      PrintDomainStart ${DOMAIN}
      OLD_IFS=$IFS # backup the default IFS
    
    while read line ; do    # Set IFS to its new value, defined by the "cdv" value
        IFS=";"
        omconfig preferences cdvformat delimiter=semicolon &>/dev/null # Set the "semicolon" as the "cdv"
        read Status Data_or_Date Description <</dev/null
        elif [[ "${Status}" = 'Non-Critical' ]] ; then
          PrintWarning "${Description:0:22}... @ ${Data_or_Date#[[:upper:]][[:alpha:]][[:alpha:]][[:space:]]}" "${Status}" "1" && RETCODE="${NonCritical}" 2>/dev/null
        elif [[ "${Status}" = 'Ok' ]] ; then
          PrintOk "${Description:0:22}... @ ${Data_or_Date#[[:upper:]][[:alpha:]][[:alpha:]][[:space:]]}" "${Status}" "1" # Do nothing !
        fi
      done << EOF
    	  $(omreport system esmlog -fmt cdv |tail -10 |grep -E "^(Ok|Non-Critical|Critical)")
    EOF
    IFS=$OLD_IFS # Back to default IFS
    
      PrintDomainEnd
      return $RETCODE
    }
    
    • Pierre B. Jul 31, 2012 @ 14:50

      Ok, i finally change that piece of *** i posted for something cleaner, and then it works.
      Just in case i post it there :

      Log_Chk() {
        # Usage: $0 called with the "log name" as $@ (as a list)
        #+ Note :only the "esmlog" is checked by this function, as it the only relevant log for hardware components status
        if [[ "${ChassisModel}" =~ 'R710' ]] ; then
          DOMAIN="ESMLOG"
          log="esmlog"
        else
          DOMAIN="ALERTLOG"
          log="alertlog"
        fi
        PrintDomainStart ${DOMAIN}
       
        while IFS=";" read Status Data_or_Date Description ; do
          omconfig preferences cdvformat delimiter=semicolon &>/dev/null # Set the "semicolon" as the "cdv"
          if [[ "${Status}" = 'Critical' ]] ; then
            PrintFailure "${Description:0:24}... @ ${Data_or_Date#[[:upper:]][[:alpha:]][[:alpha:]][[:space:]]}" "${Status}" "1" && declare -r RETCODE="${Critical}" 2>/dev/null
          elif [[ "${Status}" = 'Non-Critical' ]] ; then
            PrintWarning "${Description:0:24}... @ ${Data_or_Date#[[:upper:]][[:alpha:]][[:alpha:]][[:space:]]}" "${Status}" "1" && RETCODE="${NonCritical}" 2>/dev/null
          elif [[ "${Status}" = 'Ok' ]] ; then
            PrintOk "${Description:0:24}... @ ${Data_or_Date#[[:upper:]][[:alpha:]][[:alpha:]][[:space:]]}" "${Status}" "1"
          fi
        done <<<"$(omreport system "${log}" -fmt cdv |tail -10 |grep -E "^(Ok|Non-Critical|Critical)" |tac)"
       
        PrintDomainEnd
        return $RETCODE
      }
      
  • Bladtman Nov 16, 2013 @ 13:06

    Just a side note:
    with cat, both -e and -t specifies -v implicitly.
    So you’re actually specifying -v three times.

    cat -etv = cat -et

  • RogierD Apr 4, 2014 @ 9:47
    IFS='|' read domain ip4 ip6 <<< 'google.com|74.125.236.65|2404:6800:4007:801::1008'
    echo "$domain has $ip4 IPv4 and $ipv6 IPv6 address."
    

    There is a typo in this example, ip6 will not give an output since you define $ipv6

  • Elomar Oct 18, 2015 @ 0:05

    Dear ViVek,
    Thank you ! \o/

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre> for code samples. Still have questions? Post it on our forum