≡ 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. 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"
 
{ 7 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

  • Elomar October 18, 2015, 12:05 am

    Dear ViVek,
    Thank you ! \o/

Leave a Comment


   Tagged with: , , , ,