Pruning directory branches
How to delete a directory tree using REXX.
by Gordon Snider
One of the many challenges posed in Down To Earth REXX (William F. Schindler, PerfectNiche, 2000) is to design and write a program that deletes a directory and all of the files and subdirectories under that directory. This article shows my solution, and explains how it works.
We have all used the MD command to make a directory, and RD to remove a directory when working from the command line. But what do we do if we want to trim a whole branch? Sure, we could go to the desktop and use the graphical interface to delete the branch by dragging its root directory to the shredder, but sometimes we want to stay on the command line and work from there.
I wrote a small REXX program, RDD.CMD, that trims a whole branch at once. It uses a couple of RexxUtil functions and recursion. Also, this program could be used to uninstall an application that doesn't have its own UNINSTALL routine.
Stripped of all options and error handling, and most comments, the core of the code looks like this. (I added line numbers to make it easier for me to refer to and explain the program lines. They aren't part of the program.)
1. IF LoadRexxUtil() THEN EXIT
2. ARG target
3. CALL next target /* enable new set of variables */
4. parent = Directory(..)
5. rc = SysRmDir(target)
6. SAY 'Removed directory' target
7. EXIT 0
8. next: PROCEDURE /* do each level of each branch */
9. ARG nextdir
10. thisdir = Directory(nextdir)
/* files? */
11. rc = SysFileTree('*.*', 'file.', 'FO',,'-*---')
12. DO f = 1 TO file.0
13. rc = SysFileDelete(file.f) /* delete it */
14. END f
/* subdirs? */
15. rc = SysFileTree('*', 'dir.', 'DO',,'-*---')
16. DO d = 1 TO dir.0
17. CALL next dir.d /* recurse */
18. newdir = Directory(..) /* back to parent */
19. rc = SysRmDir(dir.d)
20. END d
21. RETURN 0 /* a leaf was reached */
Two situations prevent a subdirectory from being deleted: there are directories in the subdirectory we want to delete, or there are files there. In either of these situations, if we are in the parent directory and issue RD with the subdirectory name, the command will fail.
This is a moderately complex directory tree that I will use to explain the function of the program. Although they are not shown here, many or all of these directories could contain files.
D:\
+--COMM461
. +--PROGRAM
. ¦ +--DYNFONTS
. ¦ +--JAVA
¦ ¦ +--BIN
¦ ¦ +--CLASSES
¦ ¦ +--117
¦ ¦ +--118
¦ +--LANG
¦ ¦ +--en_US
¦ ¦ +--DEFAULTS
¦ ¦ +--NETHELP
¦ ¦ +--NETSCAPE
¦ ¦ +--COLLABRA
¦ ¦ +--SHARED
¦ ¦ +--TROUBLE
¦ +--LICENSES
¦ +--PLUGINS
¦ +--WIN16
¦ +--PLUGINS
+--SIUTIL
+--USERS
+--gsnider
+--archive
+--Cache
+--News
+--news.dir
To start off, imagine the current directory is the root of D:\ and COMM461 is one, of perhaps many, first level subdirectories.
Enter the command RDD COMM461
to prune the whole branch shown above. The numbers at the beginning of each line correspond to the line numbers in the program listing above.
- Line 1: The
LoadRexxUtil PROCEDUREis called to register functions. I used functions instead of command line commands to make error codes available and to eliminate informational messages, and for the extra power of included attribute changes. - Line 2: Pick up the name of the subdirectory to be pruned from the command line and assign it to the variable
target, in this caseCOMM461. - Line 3: Call the recursive procedure
NEXT:and pass it the subdirectory name. - Line 8: Begin the procedure and use the
PROCEDUREkeyword to set up a new set of variables in the procedure. Variables from any earlier recursions are preserved and hidden, including the loop counters. - Line 9: Assign the passed value,
COMM461, to the variablenextdir. - Line 10: Make that subdirectory, in this case
COMM461, the current directory. - Line 11: Check the directory for Files Only, and assign the name of each file found to the compound variable
file.. Each file found will have a numeric index beginning at1andfile.0will be set to the number of files found. The system, read only and archive attribute bits of any file found will be cleared so the file can be deleted. - Lines 12, 13, 14: Create a loop that deletes all files found. The first time any directory is touched by this procedure any files in the directory will be deleted.
SysFileDelete()does not take wildcards. - Line 15: Check the directory for subdirectories only,
"DO". Clear the attribute bits so the directory can be deleted. Assign the names of any subdirectories to the compound variabledir.and setdir.0to the number of subdirectories found. On this iteration with the current directory ofCOMM461dwill be set to1
dir.1will be set toPROGRAM,
dir.2will be set toSIUTIL,
dir.3will be set toUSERS,
dir.0will be set to3, the number of subdirectories found. - Line 16: Because
dir.0is greater than0a loop is set up to handle the 3 directories found. - Line 17: Because the loop has been entered, and therefore subdirectories exist, call the
NEXT:procedure recursively and pass the name of the next unhandled subdirectory, in this case,PROGRAM. - Line 8: The
PROCEDUREstatement forces the preserving and hiding of all the variable values created by theSysFileTreecalls and the position in the directory handling loop and the variablenextdir. - Line 9: Assign the passed name,
PROGRAM, to a new instance of nextdir variable. - Line 10: Change the current directory to
PROGRAM. - Lines 11, 12, 13, 14: Check for and delete all files in
PROGRAM. - Line 15: Check for subdirectories in
PROGRAM, assign their names to a new instances ofdir.. There are 6,DYNFONTS,JAVA,LANG,LICENSES,PLUGINS,WIN16. Setdir.0to6. - Line 16: Set up a loop to handle these 6 directories.
- Line 17: Begin to process
dir.1,DYNFONTS, by callingNEXTDIR PROCEDUREand passingDYNFONTSto it. Back to line 8. - Lines 8, 9: Preserve and hide the
file.anddir.variables along with the loop counters. - Line 10: Make
DYNFONTSthe current directory. - Lines 11, 12, 13, 14: Check for and delete any files.
- Line 15: Check for subdirectories. There are none. We have reached a "leaf" directory on this branch. It can be deleted.
- Lines 16, 17, 18, 19, 20: Because there are no subdirectories in
DYNFONTS, this loop is not executed. - Line 21: The first
RETURNkeyword is reached. Anyfile.variables and thedir.0variable associated withDYNFONTSare abandoned. The flow of control moves to line 18 for the first time, and the set of variables that was created whenPROGRAMwas the current directory is re-activated. However this does not change the current directory. That is done in the next step. - Line 18: The current directory is set to the parent of the leaf directory. Variable
dhas a value of1anddir.1has a value ofDYNFONTSagain, as previous variable names are re-activated. - Line 19:
DYNFONTSdirectory is removed. - Line 20: The loop ends and control goes to line 16 to see if the loop has counted up to its control value. It has not so
dir.2with a value ofJAVAis processed.
From this point, files are checked for and deleted, subdirectories are checked for and BIN and CLASSES are found. The NEXT: procedure is called. (The program is looking for the next leaf directory.) BIN is made the current directory. Files are checked for and deleted. Subdirectories are checked for and none are found. Executing the RETURN keyword reactivates the set of variables created when JAVA was the current directory and BIN is removed. Then CLASSES is processed.
In this way the program makes its way down the whole chain. There may be a simpler way to do this but it hasn't occurred to me yet.
Full Listing
The full program listing is available in rdd.cmd

