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

by on July 25, 2012 · 6 comments· LAST UPDATED April 4, 2014

in , ,

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"
 
TwitterFacebookGoogle+PDF versionFound an error/typo on this page? Help us!

{ 6 comments… read them below or add one }

1 Pierre B. July 25, 2012 at 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.

Reply

2 Pierre B. July 26, 2012 at 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
}

Reply

3 Pierre B. July 31, 2012 at 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
}

Reply

4 Bladtman November 16, 2013 at 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

Reply

5 RogierD April 4, 2014 at 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

Reply

6 Nix Craft April 4, 2014 at 3:02 pm

Thanks for the heads up!

Reply

Leave a Comment

Tagged as: , , , ,

Previous Faq:

Next Faq: