IBM Mainframe Forum Index
 
Log In
 
IBM Mainframe Forum Index Mainframe: Search IBM Mainframe Forum: FAQ Register
 

Identify unused (junk) REXX scripts


IBM Mainframe Forums -> TSO/ISPF
Post new topic   Reply to topic
View previous topic :: View next topic  
Author Message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Mon Feb 21, 2022 7:24 pm
Reply with quote

I have inherited a fiarly complex REXX 'application'.
It as spread across many tens (nearly one hundred) of files, some of which are menu definitions, and some are the actual worker scripts.

These all sit within a single PDS, but then are organized into sub-PDSs, and of course, eventually seperate DSNs

PDSs
MYUSER.dev.TheApp
MYUSER.dev.TheApp.CLIST
MYUSER.dev.TheApp.DATA
MYUSER.dev.TheApp.EXEC
MYUSER.dev.TheApp.MLIB
MYUSER.dev.TheApp.PLIB
etc.

DSNs
MYUSER.dev.TheApp.PLIB.APP001 - the main panel
MYUSER.dev.TheApp.PLIB.APP002 - a sub panel
MYUSER.dev.TheApp.PLIB.APP003 - another sub panel
MYUSER.dev.TheApp.PLIB.APP005
etc..

I am fairly certain that many of the existing DSNs are junk, and not actually used. I wish to clean down (or at least identify) the unusued DSNs
How do I do this ?
Back to top
View user's profile Send private message
sergeyken

Senior Member


Joined: 29 Apr 2008
Posts: 2022
Location: USA

PostPosted: Mon Feb 21, 2022 7:37 pm
Reply with quote

Use IDCAMS utility with series of statements like this one:
Code:
 DELETE ‘unused DSNAME’
Back to top
View user's profile Send private message
Pedro

Global Moderator


Joined: 01 Sep 2006
Posts: 2547
Location: Silicon Valley

PostPosted: Tue Feb 22, 2022 12:01 am
Reply with quote

Quote:
These all sit within a single PDS

fyi. I think you have a misunderstanding of what a PDS is. There is no such thing as a sub-PDS.

Perhaps you mean that they all have a similar naming convention.
Back to top
View user's profile Send private message
Pedro

Global Moderator


Joined: 01 Sep 2006
Posts: 2547
Location: Silicon Valley

PostPosted: Tue Feb 22, 2022 12:11 am
Reply with quote

Quote:
identify) the unused DSNs

There is no easy way to do this. My suggestion is to try to exercise the panels and keep track of what is used. Rename everything else to use a different high level qualifier and wait until somebody complains. Restore the datasets to original names as required. But this is pretty disruptive. Keep the archived files for several years.

If you can, maybe create a way to log the usage. At the beginning of each rexx and clist, update a log file with date, userid, and rexx program name. Maybe also log when a panel is about to be displayed.
Back to top
View user's profile Send private message
sergeyken

Senior Member


Joined: 29 Apr 2008
Posts: 2022
Location: USA

PostPosted: Tue Feb 22, 2022 12:44 am
Reply with quote

Pedro wrote:
Quote:
identify) the unused DSNs

There is no easy way to do this. My suggestion is to try to exercise the panels and keep track of what is used. Rename everything else to use a different high level qualifier and wait until somebody complains. Restore the datasets to original names as required. But this is pretty disruptive. Keep the archived files for several years.

If you can, maybe create a way to log the usage. At the beginning of each rexx and clist, update a log file with date, userid, and rexx program name. Maybe also log when a panel is about to be displayed.

This is a general problem for any large system. Not only for used/unused REXX/CLIST libraries, but even more serious, for multiple load modules spread over LINK/LOAD libraries during the life of the company. In most cases the administration gives up, and leaves everything as is, for future generations.

In one big government organization I was working about 8 years there was a list of 97 load libraries in the system LINKLIST, created by many system programmers in the past 40 or 50 years. There is practically no chance to distinguish between used, and never-used modules for the time being.
Back to top
View user's profile Send private message
Pedro

Global Moderator


Joined: 01 Sep 2006
Posts: 2547
Location: Silicon Valley

PostPosted: Tue Feb 22, 2022 4:12 am
Reply with quote

Quote:
created by many system programmers in the past


I had such an issue 30 years ago... for compiled programs, make sure you have the source code. Try to re-compile everything every so many years.
Back to top
View user's profile Send private message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Tue Feb 22, 2022 2:31 pm
Reply with quote

I was thinking that some kind of script could be used.
In Linux, this wouldn't be very difficult to write. In fact, it's a one-liner:

for file in $(find . -type f); do grep -rL $(basename $file); done

This at least identifies the files that do not contain the text matching the names of files in all subdirectories.

Can't something like that be done ?
Back to top
View user's profile Send private message
Garry Carroll

Senior Member


Joined: 08 May 2006
Posts: 1193
Location: Dublin, Ireland

PostPosted: Tue Feb 22, 2022 5:21 pm
Reply with quote

Again, as Pedro stated earlier, PDS does not have sub-PDSes.

Also, while you can scan libraries for members that contain specified text (the others therefore not having that text), I fail to see how that can assist you. Finding text that references another REXX member is no guarantee that the member is/was ever executed.

Garry.
Back to top
View user's profile Send private message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Tue Feb 22, 2022 5:46 pm
Reply with quote

Yeah, I understand that the PDS thing isn't exacly the same as a directory, with sub-directories, but, that's the way it's being used here.

It would be good enough for now to identify DSNs which are definitely not used - they are definitely not used if they are never referenced by any panel or other REXX in this app.

So, I need a way to iterate over all the DSNs in this App, and for each DSN bare name, I want to find out if that is ever referenced in any other DSN within the App. If not, I think I can safely delete (or at least rename it in preparation for deletion)
Back to top
View user's profile Send private message
Willy Jensen

Active Member


Joined: 01 Sep 2015
Posts: 712
Location: Denmark

PostPosted: Tue Feb 22, 2022 9:55 pm
Reply with quote

"they are definitely not used if they are never referenced by any panel or other REXX in this app" is not neccessarily true, the name could be generated dynamically.
Back to top
View user's profile Send private message
Pedro

Global Moderator


Joined: 01 Sep 2006
Posts: 2547
Location: Silicon Valley

PostPosted: Wed Feb 23, 2022 12:24 am
Reply with quote

Quote:
if they are never referenced

They could be called from within some rexx program or panel in a differently named data set.
Back to top
View user's profile Send private message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Wed Feb 23, 2022 5:35 pm
Reply with quote

Quote:
the name could be generated dynamically.

Quote:
They could be called from within some rexx program or panel in a differently named data set.


I'm not really concerned about such possibilities. I will deal with any special circumstance later. I really want to start by clearing the "Unused" REXX, so that I have a better picture of the magnitude of the App. I have a complete backup in case I really do discover such usage paradigms.
Back to top
View user's profile Send private message
Willy Jensen

Active Member


Joined: 01 Sep 2015
Posts: 712
Location: Denmark

PostPosted: Wed Feb 23, 2022 11:50 pm
Reply with quote

The job below does a any-to-any crossref. It builds a list of member for all requested libraries and then scan all those libraries for the names. The job uses the PDSMAIN / PDS86 utility from your textwww.cbttape.org file 182. If you have something else, you must modify the statements accordingly. It will probably run for a veeeery long time. You can do some member name filtering in the 1st step.
The final list will look like this:
MTM4P01 in WJENSEN.LIB.EXEC MTM
Code:
//*                                                             
//X1       EXEC PGM=IEFBR14                                     
//PDSCMD   DD UNIT=SYSDA,DISP=(MOD,DELETE),DSN=XXXXXX.PDSCMD,
//            RECFM=FB,LRECL=80,BLKSIZE=0,SPACE=(TRK,(5,5))     
//*                                                             
//A        EXEC RXINSTRM                                         
//REXX     DD *                                                 
 /* load library list  */                                       
 "execio * diskr liblist (stem lib. finis)"                     
 do n=1 to lib.0                                                 
   lib.n=strip(lib.n)                                           
 end                                                             
                                                                 
 /* make member list */                                         
 mbrn=0                                                         
 do libn=1 to lib.0                                             
   zz=outtrap('ml.')                                             
   "listds '"lib.libn"' members"                                 
   zz=outtrap('off')                                             
   say 'Listing' lib.libn 'returns' ml.0 -7 'names'             
   do n=7 to ml.0                                               
     m=strip(ml.n)                                               
     if length(m)<5   then iterate   /* filter */               
     parse value mbrn+1 m with mbrn mbr.mbrn                     
   end                                                           
 end                                                             
 mbr.0=mbrn                                                     
 say '# members before sort:' mbr.0                             
 /* note, 'sort' must be in lowercase */                         
 call bpxwunix 'sort -u', 'MBR.', 'MBR.', 'STDERR.' /* unique */
 say '# members after sort :' mbr.0                             
                                                                 
 /* generate PDSMAIN */                                         
 do ln=1 to lib.0                                               
   if ln=1 then queue "PDSMAIN '"lib.ln"'"                       
   else         queue "C     '"lib.ln"'"                         
   do mn=1 to mbr.0                                             
     queue " FIND : '"mbr.mn"'"                                 
   end                                                           
 end                                                             
 queue 'END'                                                     
 "execio" queued() "diskw pdscmd (finis)"                       
 exit 0                                                         
XMsg: if arg(1)<>'' then say arg(1);return word(arg(2) 0,1)     
//PDSCMD   DD UNIT=SYSDA,DISP=(,CATLG),DSN=XXXXXX.PDSCMD,     
//            RECFM=FB,LRECL=80,BLKSIZE=0,SPACE=(TRK,(5,5))     
//LIBLIST  DD *                                                 
XXXXXX.LIB.ISPPLIB                                             
XXXXXX.LIB.EXEC                                                 
//*                                                             
//X2       EXEC PGM=IEFBR14                                     
//SYSOUT   DD UNIT=SYSDA,DISP=(MOD,DELETE),DSN=XXXXXX.SYSOUT,
//            RECFM=VBA,LRECL=240,BLKSIZE=0,SPACE=(TRK,1)       
//*                                                             
//B        EXEC PGM=IKJEFT1B                                   
//STEPLIB  DD DISP=SHR,DSN=WJENSEN.LIB.LOADLIB   *PDSMAIN*     
//SYSTSIN  DD DISP=SHR,DSN=XXXXXX.PDSCMD                     
//SYSTSPRT DD UNIT=SYSDA,DISP=(,CATLG),DSN=XXXXXX.SYSOUT,   
//            RECFM=VBA,LRECL=240,BLKSIZE=0,SPACE=(CYL,(5,5))   
//*                                                             
//C        EXEC RXINSTRM                                       
//LIST     DD DISP=SHR,DSN=XXXXXX.SYSOUT                     
//REXX     DD *                                                 
 /* list list, generate condensed list */   
 parse value '' with dsn mbr               
 do forever                                 
   "Execio 1000 diskr list (stem lst.)"     
   if lst.0=0 then leave                   
   do ln=1 to lst.0                         
     l=substr(lst.ln,2)    /* drop asa */   
     select                                 
       when substr(l,1,8)=='PDSMAIN ',     
          | substr(l,1,6)=='PDS86 ' ,       
          | substr(l,1,6)=='C     ' then,   
           dsn=strip(word(l,2),"b","'")     
       when substr(l,1,8)==' FIND : ' then,
           mbr=strip(word(l,3),"b","'")     
       when substr(l,1,8)=='** FIND ' then,
         say left(mbr,8) 'in' dsn word(l,3)
       otherwise nop                       
     end                                   
   end                                     
 end                                       
 "Execio 0 diskr list (finis)"   
//*           

Back to top
View user's profile Send private message
Pete Wilson

Active Member


Joined: 31 Dec 2009
Posts: 580
Location: London

PostPosted: Fri Feb 25, 2022 8:37 pm
Reply with quote

Do you have FDR in your site? Let me know, I can give you some code to get the last-ref dates on the PDS's and members themselves.
Back to top
View user's profile Send private message
Pedro

Global Moderator


Joined: 01 Sep 2006
Posts: 2547
Location: Silicon Valley

PostPosted: Sat Feb 26, 2022 12:04 am
Reply with quote

Quote:
It builds a list of member for all requested libraries and then scan all those libraries for the names.

Hmmm, you use PDSMAIN... I think it would make this process more consumable if you built a SRCHFOR job.
Back to top
View user's profile Send private message
steve-myers

Active Member


Joined: 30 Nov 2013
Posts: 917
Location: The Universe

PostPosted: Sat Feb 26, 2022 4:32 am
Reply with quote

Many programs are available to list data set attributes. For example -
Code:
(DSLIST 02/22/22 13.06)                     D S L I S T   F O R   B A T C H                25 FEB 2022 17:25  PAGE     1
VOLUME  RECFM LRECL BLKSZ DSORG CREDT  XPIRDT  LASTREF   ALLOC    USED  DATA SET NAME

XXXSTG  FB       80 27920  PS  22JAN22 -----   22FEB22       2       2 ZZZZZZ.DSLIST.ASM
XXX002  VBM     137 27998  PS  22JAN22 -----   22FEB22       6       5 ZZZZZZ.DSLIST.ASMLIST
XXX003  FB       80  3200  PO  16JAN22 -----   24JAN22      13       6 ZZZZZZ.DSLIST.XXX.PDS
XXX006  VB      255  3120  PS  18OCT21 -----   22FEB22       1       1 ZZZZZZ.DSLIST.CLIST
XXX003  FB       80 27920  PO  26JAN22 -----   26JAN22      15       1 ZZZZZZ.DSLIST.EVALUATE.PDS
XXXSTG  U         0 32760  PO  22JAN22 -----   25FEB22       3       3 ZZZZZZ.DSLIST.LOAD
XXX002  FB       80 27920  PS  22JAN22 -----   22FEB22       1       1 ZZZZZZ.DSLIST.OBJ
XXX003  VB     4100 27998  PS  22JAN22 -----   22FEB22      19       5 ZZZZZZ.DSLIST.PCLIST
XXXSTG  VBA     125 27998  PS  22JAN22 -----   25FEB22       1       1 ZZZZZZ.DSLIST.SYSPRINT
XXX003  VB     4100 27998  PS  22JAN22 -----   10FEB22       1       1 ZZZZZZ.DSLIST.TEXT
XXX006  FBA     121 27951  PS  24JAN22 -----   22FEB22       1       1 ZZZZZZ.DSLIST.UPDATE.SYSPRINT
XXX006  FB       80  3120  PS  21JAN22 -----   21JAN22       1       1 ZZZZZZ.DSLIST.V10A.UPDATE.ASM
XXXSTG  FB       80  3120  PS  22JAN22 -----   22FEB22       1       1 ZZZZZZ.DSLIST.V11.UPDATE.ASM
                                                            65      29  44%
Most of these programs, like this one, "file" 1025 in the CBT collection, display the last reference date for the data set.

There is no practical way to obtain a last reference date for a member of a PDS. Members with directory entries that have ISPF statisticss will show the last date the member was updated.
Back to top
View user's profile Send private message
Willy Jensen

Active Member


Joined: 01 Sep 2015
Posts: 712
Location: Denmark

PostPosted: Sat Feb 26, 2022 2:51 pm
Reply with quote

@Pedro "I think it would make this process more consumable if you built a SRCHFOR job" you have a point, I'll give it a try. I just happen to like PDS86 a lot.
Back to top
View user's profile Send private message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Mon Feb 28, 2022 3:08 pm
Reply with quote

Willy Jensen wrote:
The job below does a any-to-any crossref. It builds a list of member for all requested libraries and then scan all those libraries for the names.....


Thank you for the response.

I must admit, after reading through this, I cannot decide if I want to laugh or cry.

It seems hard to believe that a simpler solution can't be found.

I can't really understand what is being done here anyway. What do I need to change to provide my list of PDSs ? I don't see where I provide the top-level level namespace for iteration.

[/quote]
Back to top
View user's profile Send private message
Garry Carroll

Senior Member


Joined: 08 May 2006
Posts: 1193
Location: Dublin, Ireland

PostPosted: Mon Feb 28, 2022 3:48 pm
Reply with quote

Quote:
I can't really understand what is being done here anyway. What do I need to change to provide my list of PDSs ? I don't see where I provide the top-level level namespace for iteration.


PDS and namespace are from different environments. There is no top-level other than the PDS itself.

A REXX program (A) in PDS#1 can invoke a REXX program (B) in PDS#2 which can invoke a REXX program(C) in PDS#1. REXX programs don't have to be in a PDS - REXX in a PS dataset can invoke a REXX member in a PDS & vice-versa.

Garry.
Back to top
View user's profile Send private message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Mon Feb 28, 2022 6:17 pm
Reply with quote

Garry Carroll wrote:


A REXX program (A) in PDS#1 can invoke a REXX program (B) in PDS#2 which can invoke a REXX program(C) in PDS#1. REXX programs don't have to be in a PDS - REXX in a PS dataset can invoke a REXX member in a PDS & vice-versa.

Garry.


I understand that. I am using "top level" in a logical context only.
Still, I wish I could understand how this seemingly trivial search (see my linux BASH one-liner above) can possibly require a hundred line script.
Back to top
View user's profile Send private message
Willy Jensen

Active Member


Joined: 01 Sep 2015
Posts: 712
Location: Denmark

PostPosted: Mon Feb 28, 2022 7:25 pm
Reply with quote

The script I provided will do a cross-ref between libraries in the //LIBLIST DD libref. You need to create a list of all membernames in all libraries, then scan all the libraries for those names. I'm not a Linux person, but I dont see how your one-liner does that job. Or I might have misunderstood something.
Back to top
View user's profile Send private message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Tue Mar 01, 2022 1:57 pm
Reply with quote

Willy Jensen wrote:
... but I dont see how your one-liner does that job. Or I might have misunderstood something.


# find every object of type file (-type f) within the current directory (.)
# and subdirectories (default dehavior)
for file in $(find . -type f);

# search for text (grep) recursively (-r) BUT, only show results (a file name)
# if the search text is not found (-L), where the search text is the file name
# (stripping directory name) discovered by 'find',
do grep -rL $(basename $file);

# self explanitory
done


NOTE: This is in effect 3 lines, as you can see by the use of semicolon. But, since each line is quite short, it's common (and considered good style) to place such a loop on a single line.
Back to top
View user's profile Send private message
enrico-sorichetti

Superior Member


Joined: 14 Mar 2007
Posts: 10873
Location: italy

PostPosted: Tue Mar 01, 2022 4:16 pm
Reply with quote

while the oneliner produces the proper output according to the used syntax

the output will be just useless

rexxa containing panelA
rexxb containing panelB

both will apear in the output

for portability across different grep implementations it woud be better to write
for file in $(find . -type f); do grep -rL $(basename $file) * ; done

tested on fedora and apple big sur
with both the gnu and bsd versions of grep
( in both cases grep will search every file in the current directory structure )
Back to top
View user's profile Send private message
Smouch Smith

New User


Joined: 27 Oct 2021
Posts: 17
Location: Germany

PostPosted: Tue Mar 01, 2022 4:37 pm
Reply with quote

enrico-sorichetti wrote:

the output will be just useless

rexxa containing panelA
rexxb containing panelB


I don't understand what you mean by this.

When the bash script is executed, the only output is the names of files which do not appear in other files - IOW, those files which are present for apparently no reason, they are 'junk' files cluttering up the application namespace.

Code:
# create a sample environment
$ mkdir test
$ cd test
$ mkdir dir1
$ mkdir dir2
$ mkdir dir3

# create some files
$ touch dir1/file001
$ touch dir1/file002
$ touch dir1/file003
$ touch dir2/file001
$ touch dir2/file004
$ touch dir3/file002
$ touch dir3/file004
$ touch dir3/file005

# populate a few files with other files names
$ echo file001 >> dir1/file003
$ echo file002 >> dir1/file003
$ echo file002 >> dir2/file004
$ echo file002 >> dir3/file004
$ echo file003 >> dir3/file004
$ echo file004 >> dir1/file003

# at this point the text 'file005' does not exist in any of the files created
# grep with -rL to show how that works - all the files should be listed
$  grep -rL 'file005' .
./dir1/file001
./dir1/file002
./dir1/file003
./dir2/file001
./dir2/file004
./dir3/file002
./dir3/file004
./dir3/file005

# search for a filename that does exist as text within some of the files
# those files should not appear in the result list
$  grep -rL 'file002' .
./dir1/file001
./dir1/file002
./dir2/file001
./dir3/file002
./dir3/file005

# Thus file002 is referenced, but file005 is not


So, in fact, my one-liner doesn't produce perfectly clean results, but it would be trivial to check that the list of files which does not contain file005 is the same size as the list of all files.
Back to top
View user's profile Send private message
enrico-sorichetti

Superior Member


Joined: 14 Mar 2007
Posts: 10873
Location: italy

PostPosted: Tue Mar 01, 2022 5:01 pm
Reply with quote

my point was not about the grep in se,
but using it inside the one-liner

if you are more comfortable using unix like tools
why not copy the datasets involved to the USS side an do your research from there
( natuarally depending on the amount of data to be copied )
Back to top
View user's profile Send private message
View previous topic :: :: View next topic  
Post new topic   Reply to topic View Bookmarks
All times are GMT + 6 Hours
Forum Index -> TSO/ISPF Goto page 1, 2  Next

 


Similar Topics
Topic Forum Replies
No new posts Compile Several JCL JOB Through one r... CLIST & REXX 4
No new posts Running REXX through JOB CLIST & REXX 13
No new posts Error to read log with rexx CLIST & REXX 11
No new posts SFTP Issue - destination file record ... All Other Mainframe Topics 2
No new posts isfline didnt work in rexx at z/OS ve... CLIST & REXX 7
Search our Forums:

Back to Top