≡ Menu

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.
Tutorial details
DifficultyEasy (rss)
Root privilegesNo
RequirementsNone
Estimated completion time2m
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 <<<"$IFS"

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"
 
Tweet itFacebook itGoogle+ itPDF itFound an error/typo on this page?

{ 6 comments… add one }

  • Pierre B. July 25, 2012, 9:35 pm

    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. July 26, 2012, 8:15 am

    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. July 31, 2012, 2:50 pm

      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 November 16, 2013, 1:06 pm

    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 April 4, 2014, 9:47 am
    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

Leave a Comment