As you have seen, you can use the STREAM Function to explicitly open a file yourself. Or if you read or write to a file that you haven't previously opened with STREAM, then REXX implicitly opens/creates that file for you.

In most computer languages, after you are done reading/writing to a file (ie, you plan to do no further reading/writing to it), you must "close" the file. You'll notice in our previous examples that we haven't done this. REXX keeps track of all of the files that your script has open (ie, either explicitly or implicitly), and when your script finally ends, REXX automatically closes all of those files for you. Again, REXX makes it easy for you to access files without worrying about such trivial bookkeeping tasks.

One advantage of this is if REXX aborts your script due to a SYNTAX or HALT condition, then all of your open files are closed. So your script will never have files "locked open" when it terminates.

But there is an important implication to this. What this means is that every file you read and/or write may remain open until your script ends. One problem with this scheme is if you access a lot of different files. All of these files stay open until your script ends. That can put a strain upon some file systems. Another problem with this scheme is that if you read/write a file, and then try to subsequently delete that file off of the media upon which it is stored, your attempt to delete will fail. After all, your script will still actually have the file open, thus preventing it from being deleted.

Fortunately, there is a way to close a file yourself. You can call the LINEOUT Function, passing only the name of the file (ie, omit both the position arg, and any line to write). This is a special case of LINEOUT, and means that you wish the file to be closed.

/* Write a line to a text file named "mytext.txt". */
err = LINEOUT("mytext.txt", "Some line")

/* Check for an error. */
IF err \== 0 THEN DO
Bad:
   SAY "ERROR:" STREAM("mytext.txt", "D")
   RETURN
END

/* Close "mytext.txt". Note: No position or line to write. */
LINEOUT("mytext.txt")
You can also use LINEOUT to close a file that you've been reading:
/* Read the next 3 characters of "mytext.txt". */
data = CHARIN("mytext.txt", , 3)

/* Check for an error. */
IF err \== 0 THEN DO
Bad:
   SAY "ERROR:" STREAM("mytext.txt", "D")
   RETURN
END

/* Close "mytext.txt". */
LINEOUT("mytext.txt")
Note: Some interpreters, such as Reginald, allow you to also use CHAROUT to close a file by passing only the name of the file.

One caveat with using LINEOUT to close a file is that, the only way you can be certain the file closed successfully is to trap NOTREADY. LINEOUT always returns a 0 (ie, success) when you try to close a file, regardless of whether the file actually closed. (But LINEOUT will raise NOTREADY if the file doesn't close successfully).


Using STREAM to close a file

The STREAM Function has a CLOSE command you can also use to close a file. One advantage is that STREAM's "CLOSE" command returns a more definitive error indication. If the file successfully closes, an empty string is returned. If a problem, some error string is returned.

Note: Some interpreters return the string ERROR for an error closing the file. Reginald returns ERROR: xxx where xxx is an operating system error number. An appropriate error message can be retrieved with STREAM's "D" option.

/* Read the next 3 characters of "mytext.txt". */
data = CHARIN("mytext.txt", , 3)

/* Check for an error. */
IF err \== 0 THEN DO
Bad:
   SAY "ERROR:" STREAM("mytext.txt", "D")
   RETURN
END

/* Close "mytext.txt". */
err = STREAM("mytext.txt", "C", "CLOSE")

/* Check for an error. */
IF err \== "" THEN SIGNAL Bad
Note: Successfully closing a file removes any error state on it.