ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


System Administration with ooRexx

by Howard Fosdick and Jon Wolfers
03/02/2006

Do you support Windows servers? In any environment where many hands touch the servers, you'll nearly always find wasted space. Leftover .zip and .cab files, obsolete backups and archives, and redundant copies of large files all consume tons of space. Unless you've been diligent in keeping the servers clean, you'll save gigabytes if you identify and remove a couple dozen of the largest obsolete files. Removing these files also speeds up disk backups.

Windows client machines present the same opportunity. Large media files--audio, video, pictures, and images--consume space like candy. Eliminating unneeded ones reclaims that space.

You need a utility to help identify and remove obsolete large files. This article presents a simple script that scans through a disk drive or directory tree, presents a list of the largest files, and lets you delete those you select.

Just for fun, we've written the script in the free Open Object Rexx language. This gives you a chance to explore a scripting language you might not be familiar with while coding a practical utility. The language is easy to learn and use, so you'll be able to read the script and get a feel for it from this example.

Open Object Rexx

Rexx, invented at IBM around 1980, was one of the first scripting languages. It employs a whole range of techniques to combine two language goals that normally conflict: ease of use and power.

Rexx's ease of use led to its role as the bundled scripting language of the Amiga OS and OS/2. Its power awarded it the same role on the three major mainframe operating systems--OS, VM, and VSE. Today there are several free Rexx interpreters that run on every machine, from handhelds and desktops to servers and mainframes. We discussed traditional, procedural Rexx in Rexx: Power Through Simplicity.

Related Reading

Classic Shell Scripting
Hidden Commands that Unlock the Power of Unix
By Arnold Robbins, Nelson H.F. Beebe

Open Object Rexx is a 100 percent upwardly compatible superset of "classic" procedural Rexx. Any Rexx script runs under ooRexx. ooRexx adds full object orientation. This includes classes; messaging; objects; single and multiple inheritance; data hiding and encapsulation; operator overloading and polymorphism; and a large, powerful class library.

IBM developed Open Object Rexx ten years ago and distributed it as IBM Object REXX. It is experiencing renewed interest today because IBM open-sourced the product and turned it over to the Rexx Language Association. The new ooRexx racked up a quick 24,000 downloads after its unveiling at SourceForge last March, placing it among the top 3 percent of SourceForge downloads at that time. ooRexx runs under Windows, Linux, and Unix. (Our script runs on Windows and uses a Windows GUI.)

Installation and Setup

To get started, access the ooRexx homepage. Here you'll find background information and a link to the SourceForge page from which you download ooRexx and its documentation. Download the .exe product file for Windows. Just click on the file after downloading it to install the product.

The product includes full documentation, a set of six PDF files that reside in the doc/ folder in the install directory. It also installs a samples folder that contains several dozen example ooRexx scripts. For the Windows platform, these scripts demonstrate Windows integration features including ActiveX, Object Linking and Embedding (OLE), Windows Script Host (WSH), Active Directory Services Interfaces (ADSI), and Windows Management Instrumentation (WMI).

Installing ooRexx establishes a Windows file association between files with the .rex extension and the ooRexx interpreter, so you can just double-click on a script to run it. You can also run scripts from the Windows command line. Of course, object-oriented scripts must be able to access their class libraries. ooRexx concatenates its standard class library directory to your Windows PATH variable.

Before you can run our example script to identify and delete large files, you must also download a free class library from the web site Sahananda's Rexx tools (or you can download this file from a link at the end of this article). Download the BrowseForFolder class (the file named folderBrowse.cls) from the web site and place it in the ooRexx class library folder indicated by the Windows PATH variable. This is the root of the directory tree where you installed ooRexx.

When you run our example script to identify and delete large files, it displays a panel as shown in Figure 1. This allows you to select the disk drive or directory for which you want to see the largest files.

Figure 1
Figure 1. Selecting a directory

After you select the drive or directory to work with, the program displays a sorted list of the largest files on the drive or in the directory. You can select and delete as many individual files as you like. Figure 2 shows a user preparing to delete the highlighted duplicate file. Pressing the Leave button ends the script.

Figure 2
Figure 2. Preparing to delete a duplicate file

Script Structure

All ooRexx scripts have a similar structure. Executable code starts the program. As this template shows, this code ends with an exit instruction:


/**************************************************************************/
/*  TEMPLATE -- shows how ooRexx programs are structured                  */
/**************************************************************************/

/*  Executable code goes here...                                          */

exit

::requires 'oodwin32.cls'     /* The OODialog Classes- comes w/ ooRexx    */
::requires 'folderbrowse.cls' /* Download from www.sahananda.fwbo.net/rexx*/

::class LargeFileClass subclass userdialog inherit advancedcontrols

::method Init

     /* method code goes here */

/* other Methods for the class follow . . .                               */

/* other Classes and their Methods fellow . . .                           */

Any ::requires directives follow the executable code. These statements provide access to any external classes the program needs. The previous example uses the oodwin32.cls class, which defines OODialog, the ooRexx GUI that comes with ooRexx for Windows. The second ::requires directive includes the folderbrowse class you downloaded earlier for managing the user's interaction for drive and folder selection in the program.

After the ::requires directives appear any class definitions. This program has but one:

::class LargeFileClass subclass userdialog inherit advancedcontrols

LargeFileClass extends OODialog's userdialog class. It inherits additional behaviors from OODialog's advancedcontrols class. ooRexx supports multiple inheritance to give you maximum power in reusing classes and their methods.

Any methods in the class follow the class definition. As the Template shows, the ::method directive identifies each. The code that implements the method immediately follows the ::method directive. The Template shows a single method, Init, which ooRexx runs automatically when creating a new instance of an object. Of course, a program can define any number of classes and methods.

The Script

Now that you know the structure of ooRexx scripts, you can consider the example program. Here is the script in its entirety:


/* =======================================================================*/
/* Large Files -- Find the large files on your disk to delete.            */
/* Jon Wolfers May 2005 -- freely distributable code example.             */
/* =======================================================================*/

arg No_of_files .               /* Look for arguments                     */

if  \No_of_files~datatype('w') then No_of_files=50  /* Use default if none*/

MyDialog=.LargeFileClass~new(no_of_files)
MyDialog~Execute('ShowTop')     /* Create, show & run the Windows Object  */
MyDialog~DeInstall              /* Clear Up                               */

exit


::requires 'oodwin32.cls'     /* The OODialog Classes- comes w/ ooRexx    */
::requires 'folderbrowse.cls' /* Download from www.sahananda.fwbo.net/rexx*/


::class LargeFileClass subclass userdialog inherit advancedcontrols


::method Init
/* -----------------------------------------------------------------------*/
/* Init initializes when a LargeFilesClass object is created              */ 
/* -----------------------------------------------------------------------*/
expose NoOfFiles              /* Give this variable scope of the Object   */
use arg NoOfFiles

  self~Init:super             /* We call the Super Class (userdialog)     */
                              /* Now we create the Windows Object         */
  rc=self~CreateCenter(400,200,NoOfFiles 'Largest Files on Disk',,,,
                                    'MS Sans Serif',8)
  self~InitCode=(rc=0)



::method DefineDialog
/* -----------------------------------------------------------------------*/
/* DefineDialog initializes for the user dialog                           */
/* -----------------------------------------------------------------------*/
   self~DefineDialog:super   /* We call the Super Class (userdialog)      */

   self~AddButton(1,self~SizeX-60,self~SizeY-20,50,15,'Leave','Ok','DEFAULT')
   self~AddButton(10,self~SizeX-120,self~SizeY-20,50,15,'Delete','Delete')
   self~AddListControl(11,,10,40,self~SizeX-20,self~SizeY-70,'REPORTSINGLESEL')
   self~AddText(10,self~SizeY-20,self~SizeX-140, 10,' ','CENTER',12)
   self~AddProgressBar(13,10,20,self~sizeX-20,10,'SMOOTH')
   self~AddText(10,10,self~sizeX-20,10,' ','CENTER',14)



::method run
/* -----------------------------------------------------------------------*/
/* Run gets the drive or folder to display files for, retrieves files,    */
/* sorts them, and displays them to the user.                             */
/* -----------------------------------------------------------------------*/
Expose NoOfFiles drive

FBrowser=.BrowseForFolder~new('Select root to find large files in',,,,,0)
FBrowser~Execute('ShowTop')
folder=FBrowser~folder
FBrowser~DeInstall

if folder=''
then self~cancel              /* Call the superclass cancel method to exit*/
else do
   Self~GetProgressBar(13)~SetRange(1,100) /* For Drive Info              */
   disp=Self~GetStaticControl(12)

   drive=filespec('drive',folder)          /* Get drive letter from folder*/
   Self~SetTitle(NoOfFiles 'Largest Files in' folder)       /* Dialog Title  */

   disp~SetTitle('PLEASE WAIT  ... Exploring 'folder '...')
   call sysfiletree folder||'*.*','files','FSL' /* file info-> files. stem*/
   disp~SetTitle('PLEASE WAIT  ... Sorting files ...')
                          /* files.0 holds number of files in files. stem */
   if files.0 > 1 then call SysStemSort 'files.','D',,,,20  /* Largest 1st*/
   disp~SetTitle(' ')

   list=Self~GetListControl(11)        /* Set up & populate list control  */
   if list\=.nil
   then do
      list~insertColumn(0,'Size',35,'RIGHT')
      list~insertColumn(1,'Filename',80,'LEFT')
      list~insertColumn(2,'Date',35,'LEFT')
      list~insertColumn(3,'Time',35,'LEFT')
      list~insertColumn(4,'Path',200,'LEFT')
      added=0 ; i=0
      do forever
         i=i+1                               /* Move on to next file      */
         if     i > files.0   then leave     /* No more data, so go ->    */
         if added > NoOfFiles then leave     /* Reached our target, go -> */
         parse var files.i date time size attr file     /* Get file info  */
         fn=filespec('name',file)            /* Parse out filename        */
         path=file~left(file~length - fn~length)
         if attr~right(1) = 'S' then iterate /* Ignore system files       */

         list~addrow(,,Size,fn,date,time,path)      /* Pop it in the list */
         added=added+1
      end /* DO */
   end /* DO */

   Self~SetTitle(Added 'Largest Files in' folder)   /* Update Dialog Title*/
   self~ShowDriveInfo(drive)
end /* DO */

self~Run:Super                  /* We call the Super Class (userdialog)   */



::method Delete
/* -----------------------------------------------------------------------*/
/* Delete deletes a file the user selects for deletion                    */
/* -----------------------------------------------------------------------*/
expose drive

List=Self~GetListControl(11)
item=list~Selected
if item=-1
then call errormessage 'No File is selected to delete'
else if yesnoMessage('Delete' list~itemtext(item,1) 'from' list~itemtext(item,4))
     then do                                    /* Delete the file       */
        target_file=list~itemtext(item,4)||list~itemtext(item,1)
        target_file=target_file~strip
        rc=sysfiledelete(target_file)
        if rc=0
        then do
           list~Delete(item)                    /* Remove item from list */
           self~ShowDriveInfo(drive)            /* Update drive info     */
        end /* DO */
        else call RXMessageBox 'Delete failed with RC' rc 'for' target_file
     end /* DO */



::method ShowDriveInfo
/* ----------------------------------------------------------------------*/
/* ShowDriveInfo displays the drive with its amount of free space        */
/* ----------------------------------------------------------------------*/
arg drive .
   parse value sysdriveInfo(drive) with . free total label . /* Drive info*/
   pc=(free/total)*100
   self~GetStaticControl(14)~SetTitle(drive '('||label||') -' ,
       format(pc,,2)||'% free')
   Self~GetProgressBar(13)~SetPos(pc~trunc)
return

The executable code starts the program and consists of six lines. The arg instruction reads a command-line argument that tells how many of the largest files to list in the dialog box. The if instruction sets a default value of 50.

The next three lines create a new instance of the LargeFilesClass, run the object, and clean up when done. The executable code terminates with the exit instruction. That's all there is to it. Like most OO scripts, the real work occurs in the methods and the code that defines them.

As mentioned in the Template discussion, any ::requires directives come next. The two that appear in this script refer to two external classes, oodwin32.cls and folderbrowse.cls. The ::requires directives make these classes available for the use of this script. These class files must reside in the class library or folder referenced in the Windows PATH environment variable.

The oodwin32 class is part of OODialog and installs with ooRexx. The folderbrowse class is our own; download it from Sahananda's Rexx web site for the script to run.

Any class code internal to this program comes next. This script has a single class, LargeFileClass. It extends OODialog's userdialog class and also inherits methods and attributes from its advancedcontrols class:

  ::class LargeFileClass subclass userdialog inherit advancedcontrols

The five methods in LargeFileClass follow; the ::method directives identify each one. The code that implements each method immediately follows its ::method directive.

Wherever the tilde symbol (~, which we call the twiddle) is used, the message on its right is sent to the object on its left. This runs the method, as identified by the message name, on the object. The special reference Self refers to the object in which the code itself resides.

In reviewing the code, first up is the Init method. This line in the executable code section runs the Init method when it creates the new LargeFileClass object:

    MyDialog=.LargeFileClass~new(no_of_files)

In the Init method, the expose instruction makes a method's variable accessible to the object. expose implements variable scoping in ooRexx. The use arg instruction reads in this variable. In this case, the method accesses and reads the number of files to list to the screen

  expose NoOfFiles /* Give this variable scope of the Object   */

  use arg NoOfFiles

The next three lines in the method set up the user dialog by calling its superclass, creating the Windows object, and exiting the Init method with a valid return code:

  self~Init:super /* We call the Super Class (userdialog) */

                              /* Now we create the Windows Object */

  rc=self~CreateCenter(400,200,NoOfFiles 'Largest Files on Disk',,,,
                                    'MS Sans Serif',8)
  self~InitCode=(rc=0)

The method DefineDialog appears next. This method calls the superclass (userdialog), and then adds the widgets to the dialog. It also connects the buttons to the methods that run when a user presses them. Like Init, this method is background work for the user interface.

The run method does most of the work in this program. The superclass calls it when the dialog becomes visible but does not yet handle messages (in this case, button presses). It starts with an expose instruction to make visible the number of files to list to the user and the drive involved:

  Expose NoOfFiles drive

The next several statements create and execute an object to browse the files in a folder. This code builds upon the downloaded folderbrowse class:

  FBrowser=.BrowseForFolder~new('Select root to find large files in',,,,,0) 
  FBrowser~Execute('ShowTop')
  folder=FBrowser~folder
  FBrowser~DeInstall

You can examine the code that runs the folderbrowse class. It is also a subclass of userdialog.

Next up is the code that retrieves the files in a folder. It uses the sysfiletree function, one of more than 80 functions provided with ooRexx in its included RexxUtil package. RexxUtil functions allow scripts to perform operating system functions in an OS-independent manner:

  disp~SetTitle('PLEASE WAIT  ... Exploring 'folder '...')
  call sysfiletree folder||'*.*','files','FSL' /* file info-> files. stem*/

  disp~SetTitle('PLEASE WAIT  ... Sorting files ...')

                         /* files.0 holds number of files in files. stem */

  if files.0 > 1 then call SysStemSort 'files.','D',,,,20  /* Largest 1st*/

The sysfiletree function returns a set of filenames in an array. Rexx denotes arrays by compound variables: variable names that contain one or more embedded periods. The call to sysfiletree returns filenames into the files array. Refer to elements in the array as files.1, files.2, and so on. By convention, element files.0 contains the number of items in the array, so the final line in the previous example checks files.0 to verify that the array contains more than one element. If so, it invokes the RexxUtil SysStemSort function to sort the array elements by descending file size (referring to column 20 onward). Because ooRexx is a not a case-sensitive language, you can refer to the function as sysstemsort, SYSSTEMSORT, SysStemSort, or however you feel is most clear.

After retrieving and sorting the filenames, the next code block sets them up for display in a scrollable list box. The script uses the GetListControl method to get the list object and the advancedcontrol class methods InsertColumn and AddRow to populate it.

The last line in the run method invokes the method ShowDriveInfo. This method uses the RexxUtil sysdriveInfo function to retrieve space usage information. The method displays that information along with a graphical bar that shows remaining free space.

The last method to discuss is Delete. It uses the GetListControl method of userdialog to get the list object and then other userdialog methods to manage the user's selection of files to delete. It calls the RexxUtil sysfiledelete function to delete files. After a deletion, it runs the ShowDriveInfo method to update the user's display.

Why Open Object Rexx?

This example script has a useful purpose, but in terms of what ooRexx offers it's little more than a fun script.

Explore the ooRexx distribution; it's full of goodies. ooRexx integrates with Windows through ActiveX, OLE, WSH, ADSI, and WMI. Beyond that, there's the OODialog GUI, and RexxUtil, the functions that allow scripts to issue OS commands yet remain portable across OSes. The distribution includes libraries for sockets, FTP, and advanced mathematical functions, and there hundreds of free tools on the Web for Rexx and ooRexx.

Open Object Rexx features a standardized API. This allows you to call ooRexx from applications written in compiled programming languages, or to extend ooRexx with code you write in those languages. ooRexx's API supplies interfaces for macrospace, variable pool, system exits, subcommands, halt and trace, DLLs, and queues. The product includes a runtime that lets you distribute applications without exposing source code.

Open Object Rexx presents a couple of advantages compared with other object scripting languages. First, it allows you to mix procedural and object scripting features. Those who are new to object-oriented scripting can wade into the topic at a rate at which they feel comfortable. You can solve problems in procedural terms when they work best that way, while complete object-oriented facilities are still available.

Second, Open Object Rexx links to many existing systems. These include several on which Rexx dominates, the three mainframe operating systems, OS/2, and the Amiga OS. This plays out in unexpected ways. For example, it integrates Linux and Windows into IT organizations that run mainframes. Mainframe professionals can apply their Rexx skills to ooRexx on Linux or Windows and easily move to these OSes and object-oriented programming. This also capitalizes on the large pool of former and current OS/2, Amiga, and mainframe programmers who have Rexx skills. ooRexx leverages the pool of skilled professionals and existing code while moving them to newer platforms and object-oriented scripting.

What Now?

We've tried to accomplish two goals in this article: giving you a useful disk cleanup utility and introducing Open Object Rexx. The ooRexx package comes with complete documentation and sample scripts, but here are a few more resources for further investigation:

Example Scripts

Microsoft's library of Object Rexx scripts for Windows contains about 120 ooRexx scripts.

Here are six simple tutorial Open Object Rexx scripts plus two dozen procedural scripts.

Tutorials

The ooRexx Programming Guide has a highly readable introduction.

The ooRexx web site has links to many more resources.

The book Rexx Programmer's Reference includes a brief ooRexx tutorial.

The author's RexxInfo site has plenty of other resources.

Howard Fosdick is an independent consultant who has worked with most major scripting languages.

Jon Wolfers is a software developer for the trading arm of a charity.


Return to ONLamp.com.

Copyright © 2009 O'Reilly Media, Inc.