Opening Files for I/O The open command sets up an I/O channel to either a file or a pipeline of processes. The return value of open is an identifier for the I/O channel. Store the result of open in a variable and use the variable as you used the stdout, stdin, and stderr identifiers in the examples so far. The basic syntax is: open what ?access? ?permissions? The what argument is either a file name or a pipeline specification similar to that used by the exec command. The access argument can take two forms, either a short character sequence that is compatible with the fopen library routine, or a list of POSIX access flags. Table 9-6 summarizes the first form, while Table 9-7 summarizes the POSIX flags. If access is not specified, it defaults to read. Example 9-4 Opening a file for writing. set fileId [open /tmp/foo w 0600] puts $fileId "Hello, foo!" close $fileId Table 9-6. Summary of the open access arguments.r | Opens for reading. The file must exist. | r+ | Opens for reading and writing. The file must exist. | w | Opens for writing. Truncate if it exists. Create if it does not exist. | w+ | Opens for reading and writing. Truncate or create. | a | Opens for writing. Data is appended to the file. | a+ | Opens for reading and writing. Data is appended. | Table 9-7. Summary of POSIX flags for the access argument.RDONLY | Opens for reading. | WRONLY | Opens for writing. | RDWR | Opens for reading and writing. | APPEND | Opens for append. | CREAT | Creates the file if it does not exist. | EXCL | If CREAT is also specified, then the file cannot already exist. | NOCTTY | Prevents terminal devices from becoming the controlling terminal. | NONBLOCK | Does not block during the open. | TRUNC | Truncates the file if it exists. | The permissions argument is a value used for the permission bits on a newly created file. UNIX uses three bits each for the owner, group, and everyone else. The bits specify read, write, and execute permission. These bits are usually specified with an octal number, which has a leading zero, so that there is one octal digit for each set of bits. The default permission bits are 0666, which grant read/write access to everybody. Example 9-4 specifies 0600 so that the file is readable and writable only by the owner. 0775 would grant read, write, and execute permissions to the owner and group, and read and execute permissions to everyone else. You can set other special properties with additional high-order bits. Consult the UNIX manual page on chmod command for more details. The following example illustrates how to use a list of POSIX access flags to open a file for reading and writing, creating it if needed, and not truncating it. This is something you cannot do with the simpler form of the access argument: set fileId [open /tmp/bar {RDWR CREAT}] | Catch errors from open. |
In general, you should check for errors when opening files. The following example illustrates a catch phrase used to open files. Recall that catch returns 1 if it catches an error; otherwise, it returns zero. It treats its second argument as the name of a variable. In the error case, it puts the error message into the variable. In the normal case, it puts the result of the command into the variable: Example 9-5 A more careful use of open. if [catch {open /tmp/data r}fileId] { puts stderr "Cannot open /tmp/data: $fileId" } else { # Read and process the file, then... close $fileId } Opening a Process Pipeline You can open a process pipeline by specifying the pipe character, |, as the first character of the first argument. The remainder of the pipeline specification is interpreted just as with the exec command, including input and output redirection. The second argument determines which end of the pipeline open returns. The following example runs the UNIX sort program on the password file, and it uses the split command to separate the output lines into list elements: Example 9-6 Opening a process pipeline. set input [open "|sort /etc/passwd" r] set contents [split [read $input] \n] close $input You can open a pipeline for both read and write by specifying the r+ access mode. In this case, you need to worry about buffering. After a puts, the data may still be in a buffer in the Tcl library. Use the flush command to force the data out to the spawned processes before you try to read any output from the pipeline. You can also use the fconfigure command described on page 223 to force line buffering. Remember that read-write pipes will not work at all with Windows 3.1 because pipes are simulated with files. Event-driven I/O is also very useful with pipes. It means you can do other processing while the pipeline executes, and simply respond when the pipe generates data. This is described in Chapter 16. Expect If you are trying to do sophisticated things with an external application, you will find that the Expect extension provides a much more powerful interface than a process pipeline. Expect adds Tcl commands that are used to control interactive applications. It is extremely useful for automating FTP, Telnet, and programs under test. It comes as a Tcl shell named expect, and it is also an extension that you can dynamically load into other Tcl shells. It was created by Don Libes at the National Institute of Standards and Technology (NIST). Expect is described in Exploring Expect (Libes, O'Reilly & Associates, Inc., 1995). You can find the software on the CD and on the web at: http://expect.nist.gov/ |