One of the most misunderstood commands in bash is read. I consider this a shame because it’s one of the most useful. I was reminded of this recently when someone asked me to look over a shell scripting manual they had written. When I came to the read command there was less than a page dedicated to it. This brevity gave very little understanding of how the command worked and no example of how to really use it. The classic use everyone is familiarwith is to pull in user input. While this is useful it hardly scratches the surface of the command itself.
$ read x this is a test $ echo $x this is a test
This is where everyone’s knowledge seems to end. I ask you to consider this command again though. What else does it do? What is it really doing? How can you use that to your advantage?
Did you know read takes multiple variables? There are two separators you should be aware of, an internal separator which is a space, and the external separator which is a carriage return (the enter button). Read works like this:
Stop and wait for input Receive text to a separator (space or enter) Set variable to text If inside separator, go to next variable. If no more variables are available, go to last variable if outside variable, exit
Knowing this we can add a little more to our read command….
$ read x y z this is a test $ echo $x this $ echo $y is $ echo $z a test
Oh.. no we’re getting somewhere. We can now set variables by field without having do so later in the script. But can we change the field separator? YES!!!
$ IFS="1" read x y z this1is1a1test $ echo $x this $ echo $y is $ echo $z a1test
Now we’re getting somewhere. That is pretty cool, but it’s just for user input right? Nope! this is where the read command starts to get fun. Knowing all this, you can now use the read command to set variables!
FOO="this|is|a test" IFS="|" read x y z <<< $FOO $ echo $x this $ echo $y is $ echo $z a test
Hopefully you are coming up with lots of fun ways to use this new knowledge….. but we are not done yet! Let’s consider a data file separated by pipes named ‘flowers’.
iris|purple rose|red dasie|white dogwood|white
We can now pull out each variable individually and work with it inside a loop.
while IFS="|" read FLOWER COLOR _ ; do echo "The $FLOWER is $COLOR" done <<< flowers
Try this for yourself! I’ll be posting a few other variations of this loop later, but this is fun to play around with. One of the advantages in using this method is the loop passes variables. Many methods of using while loops you’ll find you cannot set variables within it (because of the subshell). In this format that isn’t the case. The subshell is passed to the read command rather than the interior of the loop. (Yeah, I know, that’s a little confusing just remember to use this format if you are setting variables inside your loop)
You may be wondering what the underscore is for at the end of the read command. Remember from earlier the last variable you set within your read command will take all input after it. By putting an underscore as your last variable, the rest of the data will just be dropped keeping you from having extra characters. Doing this is a good (and more secure) habit to be in.