Free Hosting Online for WorkStations

< Previous | Contents | Next >

IFS

Normally, the shell performs word splitting on the input provided to read. As we have seen, this means that multiple words separated by one or more spaces become separate items on the input line, and are assigned to separate variables by read. This behavior is configured by a shell variable named IFS (for Internal Field Separator). The default value of IFS contains a space, a tab, and a newline character, each of which will separate items from one another.

We can adjust the value of IFS to control the separation of fields input to read. For ex- ample, the /etc/passwd file contains lines of data that use the colon character as a field separator. By changing the value of IFS to a single colon, we can use read to input the contents of /etc/passwd and successfully separate fields into different variables. Here we have a script that does just that:



#!/bin/bash

# read-ifs: read fields from a file FILE=/etc/passwd

#!/bin/bash

# read-ifs: read fields from a file FILE=/etc/passwd


read -p "Enter a username > " user_name file_info=$(grep "^$user_name:" $FILE) if [ -n "$file_info" ]; then

IFS=":" read user pw uid gid name home shell <<< "$file_info" echo "User = '$user'"

echo "UID = '$uid'"

echo "GID = '$gid'" echo "Full Name = '$name'" echo "Home Dir. = '$home'" echo "Shell = '$shell'"

else

echo "No such user '$user_name'" >&2 exit 1

fi

read -p "Enter a username > " user_name file_info=$(grep "^$user_name:" $FILE) if [ -n "$file_info" ]; then

IFS=":" read user pw uid gid name home shell <<< "$file_info" echo "User = '$user'"

echo "UID = '$uid'"

echo "GID = '$gid'" echo "Full Name = '$name'" echo "Home Dir. = '$home'" echo "Shell = '$shell'"

else

echo "No such user '$user_name'" >&2 exit 1

fi


This script prompts the user to enter the username of an account on the system, then dis- plays the different fields found in the user’s record in the /etc/passwd file. The script contains two interesting lines. The first is:

file_info=$(grep "^$user_name:" $FILE)

This line assigns the results of a grep command to the variable file_info. The regu- lar expression used by grep assures that the username will only match a single line in the /etc/passwd file.

The second interesting line is this one:

IFS=":" read user pw uid gid name home shell <<< "$file_info"

The line consists of three parts: a variable assignment, a read command with a list of variable names as arguments, and a strange new redirection operator. We’ll look at the variable assignment first.

The shell allows one or more variable assignments to take place immediately before a command. These assignments alter the environment for the command that follows. The effect of the assignment is temporary; only changing the environment for the duration of the command. In our case, the value of IFS is changed to a colon character. Alternately, we could have coded it this way:

OLD_IFS="$IFS" IFS=":"

read user pw uid gid name home shell <<< "$file_info" IFS="$OLD_IFS"

where we store the value of IFS, assign a new value, perform the read command, and then restore IFS to its original value. Clearly, placing the variable assignment in front of


the command is a more concise way of doing the same thing.

The <<< operator indicates a here string. A here string is like a here document, only shorter, consisting of a single string. In our example, the line of data from the

/etc/passwd file is fed to the standard input of the read command. We might won-

der why this rather oblique method was chosen rather than:

echo "$file_info" | IFS=":" read user pw uid gid name home shell

Well, there’s a reason...


You Can’t Pipe read

While the read command normally takes input from standard input, you cannot do this:

echo "foo" | read

We would expect this to work, but it does not. The command will appear to suc- ceed but the REPLY variable will always be empty. Why is this?

The explanation has to do with the way the shell handles pipelines. In bash (and other shells such as sh), pipelines create subshells. These are copies of the shell and its environment which are used to execute the command in the pipeline. In our example above, read is executed in a subshell.

Subshells in Unix-like systems create copies of the environment for the processes to use while they execute. When the processes finishes the copy of the environ- ment is destroyed. This means that a subshell can never alter the environment of its parent process. read assigns variables, which then become part of the envi- ronment. In the example above, read assigns the value “foo” to the variable RE- PLY in its subshell’s environment, but when the command exits, the subshell and its environment are destroyed, and the effect of the assignment is lost.

Using here strings is one way to work around this behavior. Another method is discussed in Chapter 36.


Top OS Cloud Computing at OnWorks: