Page 1 of 1

Finding members having duplicate strings

PostPosted: Wed Sep 23, 2015 12:19 pm
by shivanshu26shiv
In my shop, many jobs in the pds are having duplicate stepnames, so if any job abends at step ABC and operator puts a RESTART=ABC after checking the abend
then the job is executing from first ABC step not the second one which was desired.

Hence I need to find those jobs that are having duplicate steps,

Sample:
//ABS EXEC ABCD,
// PARMEM=ABCDE,

I know that DFSORT is taking one member at a time, but can we use REXX to change members...?

P.S: I am not aware whether I should put this query in REXX or DFSORT forum but a solution or hint using DFSORT is preferable.

Re: Finding members having duplicate strings

PostPosted: Mon Sep 28, 2015 5:02 pm
by BillyBoyo
Here's on way you can do it with DFSORT.

First, this step:

//PUNPDS   EXEC PGM=IEBPTPCH
//SYSPRINT DD  SYSOUT=*
//SYSIN    DD  *
 PUNCH TYPORG=PO,MAXNAME=1,MAXFLDS=1
 RECORD FIELD=(80)
//SYSUT1 DD DISP=OLD,DSN=your pds
//SYSUT2 DD DSN=&&PDSUNL,
// DISP=(,PASS),UNIT=SYSDA,SPACE=(CYL,(1,1))
//*


You may need to adjust the SPACE parameter, I tested on a PDS with not many more than 100 members, and not very big ones.

That uses the IBM Utility IEBPTPCH, which "prints" or "punches" a dataset, which can be a PDS/PDSE (TYPORG=PO).

The output will be 81-byte records with a leading "control character", which we'll ignore. Each member is prefixed by a piece of text naming the member.

The output will include all members, it is a PDS/PDSE that has been "flattened" to a sequential dataset.

Then process that dataset with SORT.

//FINDDUPS EXEC PGM=SORT
//SYSOUT   DD SYSOUT=*
//SORTOUT  DD SYSOUT=*
//SORTIN   DD DSN=&&PDSUNL,DISP=(OLD,PASS)
//SYMNAMES DD *
RECORD-FIRST-TWO,2,2,CH
RECORD-FIRST-THREE,=,3,CH
PUNCH-CHAR,1,1,CH
WHOLE-RECORD,*,80,CH
RECORD-EXTENSION,*
EXT-MEMBER-NAME,*,8,CH
EXT-SEQUENCE,*,5,CH
POSITION,WHOLE-RECORD
DATA-IN-RECORD,=,72
MEMBER-POSITION,=,13,CH
MEMBER-NAME,*,8,CH
OUT-MEMBER-NAME,1,8,CH
OUT-STEP-NAME,*,8,CH
OUT-SEQUENCE,*,5,CH
OUT-WHOLE-RECORD,*,80,CH
OUT-SHOULD-BE-EXEC,*,4,CH
OUT-EXT,*
OUT-STEP-SEQUENCE,*,3,CH
POSITION,OUT-MEMBER-NAME
OUT-SORT-KEY,=,16,CH
STEP-NAME,%00
SHOULD-BE-EXEC,%01
JCL-START,C'//'
EXEC-FOR-TEST,C'EXEC'
EXEC-STATEMENT,C' EXEC '
JCL-COMMENT-OR-JES-COMMAND,C'//*'
START-OF-MEMBER,C'MEMBER NAME  '
ZERO-TO-IGNORE,C'00000'
MEMBER-NAME-CARD,C'00001'
FIRST-STEP-IN-JOB,C'001'
A-BLANK,C' '
ALL-8-BLANK,C'       '
//SYMNOUT DD SYSOUT=*
//SYSIN    DD *
                                                             
  INCLUDE COND=(RECORD-FIRST-TWO,
                 EQ,
                JCL-START,
               AND,
                (RECORD-FIRST-THREE,
                  NE,
                 JCL-COMMENT-OR-JES-COMMAND,
                 AND,
                 (DATA-IN-RECORD,SS,
                   EQ,
                  EXEC-STATEMENT)),
                OR,
                 (MEMBER-POSITION,
                   EQ,
                  START-OF-MEMBER))
                                                             
  INREC IFTHEN=(WHEN=GROUP,
                BEGIN=(MEMBER-POSITION,
                      EQ,
                       START-OF-MEMBER),
                PUSH=(RECORD-EXTENSION:
                       MEMBER-NAME,
                      SEQ=5)),
        IFTHEN=(WHEN=INIT,
                 PARSE=(STEP-NAME=(STARTAFT=JCL-START,
                                   ENDBEFR=A-BLANK,FIXLEN=8),
                       SHOULD-BE-EXEC=(SUBPOS=1,
                                       STARTAFT=BLANKS,
                                       ENDBEFR=A-BLANK,
                                       FIXLEN=4))),
        IFTHEN=(WHEN=INIT,
                 BUILD=(EXT-MEMBER-NAME,
                        STEP-NAME,
                        EXT-SEQUENCE,
                        WHOLE-RECORD,
                        SHOULD-BE-EXEC)),
        IFTHEN=(WHEN=(OUT-SEQUENCE,
                       EQ,
                      MEMBER-NAME-CARD),
                 OVERLAY=(OUT-MEMBER-NAME:
                           ALL-8-BLANK)),
        IFTHEN=(WHEN=(OUT-SHOULD-BE-EXEC,
                       NE,
                      EXEC-FOR-TEST),
                 OVERLAY=(OUT-SEQUENCE:
                           ZERO-TO-IGNORE))
                                               
  SORT FIELDS=(OUT-SORT-KEY,A)
                                               
  OUTREC IFTHEN=(WHEN=GROUP,
                  KEYBEGIN=(OUT-SORT-KEY),
                  PUSH=(OUT-STEP-SEQUENCE:
                         SEQ=3))
                                               
  OUTFIL INCLUDE=(OUT-SEQUENCE,
                   GT,
                  MEMBER-NAME-CARD,
                 AND,
                  OUT-STEP-SEQUENCE,
                   GT,
                  FIRST-STEP-IN-JOB),
         HEADER1=('DUPLICATE STEPS PER MEMBER')


I've used SORT symbols (defined on the SYMNAMES dataset, and listed on the SYMNOUT dataset) to make this easier to follow.

Firstly, INCLUDE COND= is used to select only the data which may be of use to you. This is the generated member-name card, and any JCL cards (start with // and not with //*) that may contain EXEC, bounded by space to avoid false hits.

This will give you cards like this

MEMBER NAME  XXXXXXX
// EXEC EXEC PGM=X
//STEP EXEC PGM=X
//INPUT DD DSN=ABC.DEF.GHI, THE OUTPUT FROM THE PREVIOUS EXEC


The inline comments on JCL conspire against you, but we'll deal with those.

Define a GROUP on identifying the MEMBER NAME. Save the member-name itself to an extension to the current record, along with a sequence number within the group.

Use PARSE to get the stepname, and to get the second "word" on the record. Note the SUBPOS=1. This is necessary because a single blank ending the step-name can also be the only blank available to delimit the second word on the line, so the parse-pointer has to be set back one.

BUILD a new record, with the member-name, step-name, sequence number, whole line, and the second word on the line.

Then there are two cases we want to "clobber" so they don't get selected later. The member-name record is no longer needed, and any lines which happen to have EXEC (from the INCLUDE COND) but where it is not the second word on the line (from the PARSE).

SORT the records.

Use OUTFIL to select what will only be the records with steps, and only those steps whose names are duplicate (all the duplicates will be listed, but not the original - one output line with a step will indicate two source lines, two output lines for the same step would indicate three source lines). List out the data with a simple heading.

If you are keener on old-style SORT Control Cards, here they are after the conversion of the symbols:

 INCLUDE COND=(2,2,CH,EQ,C'//',AND,(2,3,CH,NE,C'//*',AND,(2,72,SS,EQ,C'*
                EXEC ')),OR,(2,13,CH,EQ,C'MEMBER NAME  '))             
 INREC IFTHEN=(WHEN=GROUP,BEGIN=(2,13,CH,EQ,C'MEMBER NAME  '),PUSH=(82:*
               15,8,SEQ=5)),IFTHEN=(WHEN=INIT,PARSE=(%00=(STARTAFT=C'//*
               ',ENDBEFR=C' ',FIXLEN=8),%01=(SUBPOS=1,STARTAFT=BLANKS,E*
               NDBEFR=C' ',FIXLEN=4))),IFTHEN=(WHEN=INIT,BUILD=(82,8,%0*
               0,90,5,2,80,%01)),IFTHEN=(WHEN=(17,5,CH,EQ,C'00001'),OVE*
               RLAY=(1:C'       ')),IFTHEN=(WHEN=(102,4,CH,NE,C'EXEC'),*
               OVERLAY=(17:C'00000'))                                   
 SORT FIELDS=(1,16,CH,A)                                               
 OUTREC IFTHEN=(WHEN=GROUP,KEYBEGIN=(1,16),PUSH=(106:SEQ=3))           
 OUTFIL INCLUDE=(17,5,CH,GT,C'00001',AND,106,3,CH,GT,C'001'),HEADER1=('*
               DUPLICATE STEPS PER MEMBER')