Flylib.com

Books Software

 
 
 

File Descriptors


File Descriptors

A file descriptor is a single-digit integer that is assigned to an open file. Input and output operations always reference the file descriptor, not the actual file name . Qshell uses file descriptors 0, 1, and 2 for stdin, stdout , and stderr respectively. All three of these file descriptors are assigned to the terminal unless redirected to other files.

Taking explicit control of the file descriptors in a shell script is a rather infrequently used and somewhat complex feature. You might wonder why you would use this feature.

You might need to maintain access to many open files for input and output at the same time. You might be trying to retrofit existing programs or tools that use file or socket descriptors. You might be trying to swap devices so that an application's input and output descriptors match some reasonably complex requirements.

In some cases, the simpler solution of common redirection will serve you well. In other cases, you might need to take direct control of the file descriptors that Qshell maintains and uses for the utilities it invokes.

Redirection Operators

File descriptors 3 through 9 are available for you to use as you wish. To use these file descriptors, you must use the redirection operators listed in Table 10.10. The letters m and n in the table represent file-descriptor numbers .

Table 10.10: File-Descriptor Redirection Operators

Operator

Description

[n] <

Redirect input.

[n] < &m

Duplicate input. (Merge input streams.)

[n] <&-

Close input.

[n] <<, [n] <<-

Open a "here" document.

[n] >

Replace output.

[n] >

Replace output; ignore the noclobber option.

[n] >>

Append output.

[n] >& m

Duplicate output (Merge output streams.)

[n] >&-

Close output.

At least some of these should look familiar to you because Table 10.10 is a more complete version of Table 10.1. The file descriptor preceding the redirection operator is optional. If you do not code a file descriptor, Qshell assumes file-descriptor 0 for input operations and file-descriptor 1 for output operations.

Therefore, the following two tr commands are equivalent:

tr  [:lower:]  [:upper:]  <goodoleboys.txt  >upper.txt
tr  [:lower:]  [:upper:]  0<goodoleboys.txt 1>upper.txt

The Ulimit Utility

One of the features added to V5R2 Qshell was the ulimit utility, which displays or sets system resources. The n option allows you to display or set the maximum number of file descriptors that a process can open, as in the following example:


ulimit -n


200


/home/jsmith $

Opening and Closing File Descriptors

To open and close files under file descriptors for the current Qshell session or running script, use the exec utility with the open and close redirection operators. Here are some examples:

  • Open file goodoleboys.txt for input as file descriptor 3:

    exec 3<goodoleboys.txt
    

  • Open a file as input under file descriptor 9 that is named in positional parameter 1:

    exec 9<
    

  • Open stdin (file descriptor 0) to file temp.txt:

    exec < temp.txt
    

  • Close stdin (file descriptor 0):

    exec <&-
    

  • Close stdout (file descriptor 1):

    exec >&-
    

  • Close the file opened under file descriptor 3:

    exec 3<&-
    

  • Open file descriptor 7, assigning it to stdout:

    exec 7>&1
    

    Notice that an ampersand, not a dollar sign, precedes the numeral 1. Both file descriptors 1 and 7 point to stdout. The two output streams are merged.

  • Assign stderr to wherever stdout is assigned. The two output streams are merged:

    exec 2>&1
    

You can use the test utility to determine whether or not a file descriptor is open and associated with a terminal, as the following example shows:

if [ -t 0 ]
then
     print "Enter name,age,phone."
fi

If stdin is assigned to the keyboard, Qshell displays the message "Enter name, age, phone." However, if stdin is redirected to another file or if input is coming from a pipe, Qshell does not output the prompting message.

Input through File Descriptors

To read from a file, use the read utility with the -u option. Follow the -u with the number of the file descriptor from which to read. You can separate the option and the file descriptor number, but you don't have to, so the following two commands are equivalent:

read -u 5 line
read -u 5 line

Output through File Descriptors

There are two ways to write to a file descriptor: an easy way and an easier way. You need to know both methods because the easy way works in all cases, but the easier way only works with the print utility.

The easier way is to specify the file descriptor number in the -u (unit) option of the print utility:

print -u 3 $name

The easy method is to use a duplication redirection operator (>) with any Qshell command that writes to stdout. Follow the > operator with an ampersand and a file descriptor number. Think of the ampersand as meaning "whatever is assigned to." So, you might read the string >&3 as "write to whatever is assigned to file descriptor 3." The > operator usually clears the file, but for file descriptors 3 and above, Qshell appends the data instead.

Since both methods work for print , the following two statements do the same thing:

print -u 7 $name
print $name >&7

Each command writes the value in the variable name to the file opened under file descriptor 7.

In Figure 10.26, the first exec opens member TEMP in source physical file JSMITHS /QRPGSRC for output with file descriptor 3. The two echo commands write to the source member. The second exec closes the file. The cat command shows that the two lines are in the source physical file member, as they should be.

start figure


exec 3>/qsys.lib/jsmith.lib/qrpgsrc.file/temp.mbr


echo "line 1" >&3


ecbo "line 2" >&3


exec 3>&-


cat /qsys.lib/jsmith.lib/qrpgsrc.file/temp.mbr


line 1


line 2


/home/JSMITH $

end figure

Figure 10.26: Qshell output is directed to a source physical file member via a file descriptor.

In the following example, if the first parameter is missing, a message is sent to stderr in order to demonstrate proper usage for the script:

if [ -z  ]
  then
    print -u2 "Usage: $(basename
if [ -z $1 ] then print -u2 "Usage: $(basename $0) file ..." exit 1 fi
) file ..." exit 1 fi

Without the redirection operator, the print statement would send the output to stdout.

Figure 10.27 builds an FTP script that will be used for logging into an FTP server and exchanging files. First, it reads file tkmjzzypokrtmmoos to get the user ID and file jnnoiiesokjlxrros to get the password. It uses file descriptor 3 for both files, as well as the ftpmodel file, which contains the miscellaneous FTP commands. When it encounters the special commands *get and *put in the model file, the script reads the names of files to get and put from two other files, ftpgetlist.txt and ftpputlist.txt. All output is sent to stdout.

start figure

#!/bin/qsh

# get user id from text file tkmjzzypokrtmmoos
exec 3<tkmjzzypokrtmmoos
read -u 3 id
exec 3<&-

# get password from text file jnnoiiesokjlxrros
exec 3<jnnoiiesokjlxrros
read -u 3 pass
exec 3<&-

# build signon record for remote ftp
print $id $pass

# read ftp commands from model file ftpmodel.txt
exec 3<ftpmodel.txt
while read -u3 modelline
   do
      if [ "$modelline" = '*put' ] ; then
         exec 4<ftpputlist.txt
         while read -u4 dline
            do
               print put $dline
            done

         exec 4<&-
      elif [ "$modelline" = '*get' ] ; then
         exec 4<ftpgetlist.txt
         while read -u4 dline
            do
               print get $dline
            done
         exec 4<&-
      else
         print $modelline
      fi
  done
exec 3<&-

# normal end
exit 0

end figure

Figure 10.27: This script builds an FTP script from five IFS files.

Figure 10.28 shows the input files and the output produced by the script in Figure 10.27.

start figure

Input: tkmjzzypokrtmmoos

myid

Input: jnnoiiesokjlxrros

mypass

Input: ftpmodel.txt

namefmt 1
lcd /home/mydir
cd /home/yourdir
*put
*get
quit

Input: ftpgetlist.txt

inputa
inputb

Input: ftpputlist.txt

data1
data2
data3
data4
data5

Output:

myid mypass
namefmt 1
lcd /home/mydir
cd /home/yourdir
put data1
put data2
put data3
put data4
put data5
get inputa
get inputb
quit

end figure

Figure 10.28: Here is the input and output of the ftpbuild.qsh script.

Redirection and Compound Statements

Qshell permits redirection with scripts, compound statements, and sub- shells , as well as with individual statements. Using redirection a single time can improve the performance of multiple statement operations by avoiding additional Qshell file operations.

The following two examples of using while loops with redirection operators are functionally equivalent.

while read line
do echo $line >> temp.txt
done  <goodoleboys.txt
while read line
do echo $line
done  <goodoleboys.txt >> temp.txt

The content of file goodoleboys.txt is appended to the output file temp.txt one line at a time. The first example redirects the output of the echo command, while the second redirects the output of the entire loop. The example that does the redirection of the entire loop is much faster because it only opens the output file once. The other example causes Qshell to process ten file-open requests .

You may direct input and/or output by placing the redirection after the done end-of-loop delimiter or at the end of any other multi-statement block.

Figure 10.29 shows the use of redirection operators with for loops. The first loop numbers and lists the positional parameters. The second loop is identical to the first, except that the output of the loop is piped into the sort utility.

start figure

{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}


pos=0


for i


do


let pos=pos+1


echo $i \($pos\)


done


mydata.txt (1)


yourdata.txt (2)


hisdata.txt (3)


herdata.txt (4)


ourdata.txt (5)


theirdata.txt (6)


pos=0


for i


do


let pos=pos+1


echo $i \($pos\)


done  sort


herdata.txt (4)


hisdata.txt (3)


mydata.txt (1)


ourdata.txt (5)


theirdata.txt (6)


yourdata.txt (2)

end figure

Figure 10.29: Qshell permits redirection of the output produced within any loop.

The example in Figure 10.30 is identical to Figure 10.27, except that file descriptors are not used. Instead, stdin and stdout are redirected with each command, including the loops.

start figure

#!/bin/qsh

# get user id from text file tkmjzzypokrtmmoos
read id <tkmjzzypokrtmmoos

# get password from text file jnnoiiesokjlxrros
read pass <jnnoiiesokjlxrros

# build signon record for remote ftp
print $id $pass

# read ftp commands from model file ftpmodel.txt
while read modelline
   do
      if [ "$modelline" = '*put' ] ; then
         while read dline
            do
               print put $dline
            done <ftpputlist.txt
      elif [ "$modelline" = '*get' ] ; then
         while read dline
            do
               print get $dline
            done <ftpgetlist.txt
      else
         print $modelline
      fi
   done <ftpmodel.txt

# normal end
exit 0

end figure

Figure 10.30: Like the script in Figure 10.27, this script produces an FTP script. However, this script uses redirection of loops rather than file descriptors.