|
View previous topic :: View next topic
|
| Author |
Message |
sergeyken
Senior Member

Joined: 29 Apr 2008 Posts: 2264 Location: USA
|
|
|
|
Very unexpectedly I received a private(??) message from a forum member.
| Quote: |
Hello there,
We have many JCL which are using symbolics like
Code:
| Code: |
//DBPTEMP JOB (CS-P-CG-CAD-0),'DBA-DB2-JOB',CLASS=C,
//** RESTART=STEP####, <<< REMOVE * AND POINT TO RESTART STEP
// MSGCLASS=N,MSGLEVEL=(1,1),REGION=0M ,PERFORM=216
//*
//*********************************************************************
//* RESTART INSTRUCTION :
//* DO NOT RESTART IN CASE OF ABENDS, CONTACT DB2 DBA ONCALL
//*
//*********************************************************************
//* THIS JOB BUILDS
//*********************************************************************
//*
//PROCSTMT JCLLIB ORDER=(DBP.SUP.DBA.PROC,
// CCP.BASE.PROCLIB)
//*
// EXPORT SYMLIST=(DB2SS,EPFIX,SI,JBNM)
// SET SHLQ='SYSGA' <<HLQ OF SYSTEM FILES - LPAR DEPENDENT
// SET DB2SS='A' <<SET DB2 SUBSYSTEM
// SET SI='A0P' <<SCHEMA IDENTIFIER
// SET EPFIX='DBQ' <<ENVIRONMENT LEVEL
// SET JBNM='D2PDR00' <<JOBNAME FOR EMAIL STEPS
// SET RNDT='YYYY-MM-DD' << RUN DATE, INVALID DATES USE CURRENT DATE
//* << USE TO TEST A SPECIFIC DATE
// SET LRC=' ' << FORCE LIST OF REORG_CONTROL BPOOL REPORT
//* << DDNAME IS BPCOLLFL IF THE VALUE IS 'R'
// SET TF=' ' << TRACE FLAG - FOR TESTING THE PROGRAM
// SET FC='4' << FUTURE CHECK - TODAY + 4 DAYS
// SET CHKPGM=D6REOCHK << NEW VERSION STORED PROC REBINDS 04/23/24
//* PARM='&RNDT.&SI.&FC.&LRC.&TF.'
//**
//JOBLIB DD DISP=SHR,DSN=&SHLQ..DB2&DB2SS..SDSNEXIT
// DD DISP=SHR,DSN=&SHLQ..DB2&DB2SS..SDSNLOAD
.. |
Now what we are planning to change jobs to remove symbolics and use actual value... I have started working with REXX but with that single line of jcl is had to read multiple time to change value
I think it cna be done through using symname like passing symname as parm but not able to proceed further...
Kindly advise.
_________________
Cheers
Ekta |
After my suggestion to post any request as a regular forum's topic, there was no response received from this author...
Though the final goal of this request looks a little bit senseless, the process itself may be interesting.
I mentioned to the author, that such conversion hardly can be done using SORT features, but quite easily can be implemented in REXX.
I don't want to give the complete solution, especially after the rude and illiterate message, but I can present the hi-level approach here, while hiding the more detailed code from this post. I hope that either the author, or others would be interested, and can suggest their own implementation of currently hidden low-level REXX procedures.
| Code: |
/* REXX */
/*===================================================================*/
/* Substitute JCL variables */
/*===================================================================*/
"EXECIO 0 DISKR SYSIN (OPEN"
"EXECIO 0 DISKW SYSOUT (OPEN"
VarList = '' /* list of found JCL variable names */
Do #REC = 0 BY 1
"EXECIO 1 DISKR SYSIN"
If RC > 0 Then Leave #REC
Parse Pull 1 Record 72 RecEnd 81 .
Say Record /* source line to trace and debug */
Call TryScanVar /* verify all possible substitutions &XXX. */
Call CheckSetVar /* verify possible // SET XXX='1234' statement */
/* push cnahged/unchanged record back to stack */
Push Substr( Record, 1, 71 ) || RecEnd
"EXECIO 1 DISKW SYSOUT"
End #REC
"EXECIO 0 DISKR SYSIN (FINIS"
"EXECIO 0 DISKW SYSOUT (FINIS"
Say #REC "records processed"
Say "VarList = '" || VarList || "'"
Call ListFoundVars
Exit
/*===================================================================*/
TryScanVar: /* try to detect ...&NAME... and to put it's value */
. . . . . . . . . . to be continued . . . . . . . . . .
Return
/*===================================================================*/
CheckSetVar: /* try to find "//... SET NAME='VALUE' or NAME=VALUE */
. . . . . . . . . . to be continued . . . . . . . . . .
Return
/*===================================================================*/
ListFoundVars:
Do I = 1 To Words(VarList)
VarName = Word( VarList, I )
Say VarName || "='" || VarValue.VarName || "'"
End I
Return
/*===================================================================*/
|
|
|
| Back to top |
|
 |
sergeyken
Senior Member

Joined: 29 Apr 2008 Posts: 2264 Location: USA
|
|
|
|
Since no reaction received, I just post here my solution, slightly improved.
Maybe there is someone to add some comments, or suggestions...
| Code: |
/* REXX */
/*===================================================================*/
/* Substitute JCL variables */
/*===================================================================*/
"EXECIO * DISKR SYSIN (FINIS" /* read whole input into stack */
VarList = '' /* list of found JCL variable names */
TotalLines = Queued()
Do TotalLines /* process stack from top to bottom */
Parse Pull 1 Record 72 RecEnd 81 . /* unused part from pos 72 */
Say Record /* source line to trace and debug */
Record = TryScanVar(Record) /* make all substitutions &XXX. */
Call CheckSetVar(Record) /* verify // SET XXX='1234' statement */
/* queue changed/unchanged record at the stack end */
Queue Substr( Record, 1, 71 ) || RecEnd /* append the tail back */
End
"EXECIO * DISKW SYSOUT (FINIS" /* write whole output from stack */
Say TotalLines "lines processed"
Say "VarList = '" || VarList || "'"
Call ListFoundVars
Exit
/*===================================================================*/
TryScanVar: Procedure Expose VarList VarValue.
/* try to detect ...&NAME... and to put it's value */
Parse Arg LineToScan /* keep upper/lower case as is */
If Words(VarList) = 0 /* no known variables? */
Then Return LineToScan /* nothing to change yet */
NewLine = "" /* prepare to collect updates */
Do #Var = 0 By 1
/* split "...AAA&XXX..." into "...AAA" and "XXX..." */
Parse Value LineToScan ,
With 1 LeftPart '&' LineToScan
NewLine = NewLine || LeftPart /* left part is unchanged */
If LineToScan = " " Then Leave #Var /* no more &NAME */
KnownNames = Words(VarList)
Do I = 1 To KnownNames /* check known vars, one by one */
OldName = Word( VarList, I )
LenName = Length( OldName )
NameCandidate = Left( LineToScan, LenName )
If NameCandidate == OldName /* with matching case */
Then Do /* try to check matching variable name */
NextChar = Substr( LineToScan, LenName + 1, 1 )
/* check if this variable name is longer
than the one to verify?
Like: ...&X123... instead of ...&X1... ? */
If 0 < Pos( NextChar, ,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_$#@0123456789" )
Then Iterate I /* ignore longer variable name */
/* if variable found, substitute its value */
LineToScan = Substr( LineToScan, LenName + 1)
If Left( LineToScan, 1) = '.' /* "&NAME." - ? */
Then LineToScan = Substr( LineToScan, 2)
Say "Found" OldName || "='" || VarValue.OldName || "'"
NewLine = NewLine || VarValue.OldName
Leave I /* no more NAME verification needed here */
End
End I
If I > KnownNames /* if the variable has not been set, */
Then NewLine = NewLine || '&' /* then leave it as is */
End #Var
Return NewLine
/*===================================================================*/
CheckSetVar: Procedure Expose VarList VarValue.
/* try to find "//... SET NAME='VALUE' or NAME=VALUE" */
Parse Arg LineToParse /* keep upper/lower case as is */
Parse Value LineToParse ,
With 1 Prefix OpCode JCLParams
If Left( Prefix, 2) = "//" ,
& Left( Prefix, 3) /= "//*" ,
& OpCode == "SET" /* "set" is not "SET" - JCL rule */
Then Do /* process JCL SET operator */
Parse Value JCLParams With NewName '=' NewParam
If NewName /= Translate(NewName)
Then Return /* ignore not uppercase names */
If WordPos( NewName, VarList ) = 0
Then VarList = VarList NewName /* keep non-duplicated name */
If Left( NewParam, 1) = "'"
Then Parse Value NewParam With "'" RealValue "'" .
Else Parse Value NewParam With RealValue .
Say "Set" NewName || "=" || RealValue
VarValue.NewName = RealValue /* save or replace the value */
End
Return
/*===================================================================*/
ListFoundVars:
Do I = 1 To Words(VarList)
VarName = Word( VarList, I )
Say VarName || "='" || VarValue.VarName || "'"
End I
Return
/*===================================================================*/
|
|
|
| Back to top |
|
 |
Willy Jensen
Active Member

Joined: 01 Sep 2015 Posts: 772 Location: Denmark
|
|
|
|
"Since no reaction received," Ok, I'll bite. Here is my suggestion.
It may not cross all the 'T's, but works for the sample given.
Data is read using my own program, RXExecIo, just substitute with whatever input means that you fancy. The generated records are just show, again replace with the write feature that you like.
The program stores the SET values in a stem, which is reset when a JOB statement is encountered, thus enabling change of a series of jobs unloaded to a single dataset.
| Code: |
/*----------------------------------------------------------------rexx
JCL variable substitution
Temp dsns (&&name) are ignored.
&VAR where value isn't set, is marked.
--------------------------------------------------------------------*/
set.=''
"delstack"
Call RXExecIo 'get dsn(AA.TEST.JCL.DS) stem(jcl.)' /* my READ pgm */
say 'JCL records read:' 0+jcl.0
do j#=1 to jcl.0 /* over JCL input */
if left(jcl.j#,2)='//' & substr(jcl.j#,3,1)<>'*' then do
if word(jcl.j#,2)='JOB' then do
say 'New job' substr(word(jcl.j#,1),3)
set.=''
queue jcl.j#
end
else if word(jcl.j#,2)='SET' then Call GetSet strip(left(jcl.j#,72))
else if pos('&',jcl.j#)>0 then Call Subst strip(left(jcl.j#,72))
else queue jcl.j#
end
else queue jcl.j#
end
say ''
say 'Generated...'
do queued()
parse pull r
say r
end
"delstack" /* not really neccessary */
Exit 0
GetSet: /* locate var defs, save values */
parse value arg(1) with r . 'SET' n . '=' v
if left(v,1)="'" then parse var v =2 v "'" .
else parse var v v .
set.n=v
queue insert('*',arg(1),2) /* make SET a coment */
return 0
Subst: /* locate variables in JCL, replace with stored value */
ir=arg(1)' '
say 'subst in: ' arg(1)
or=''
do forever
p=pos('&',ir)
if p=0 then leave
if substr(ir,p+1,1)='&' then do /* temp dsn */
or=or'&&'
ir=substr(ir,3)
iterate
end
parse var ir f '&' ir /* get front, name and back */
or=or || f
p=Verify(ir,'.,() ','m') /* pos at first of list */
parse var ir v =(p) ir
if left(ir,2)='..' then ir=substr(ir,2)
if set.v<>'' then v=set.v
else do
say '*' v 'is not SET'
v='?'v'?'
end
or=or || v
end
or=or || ir /* add whats left */
say 'subst out:' or
return qjcl(or)
QJcl: /* queue JCL, possible split into multiple records */
parse arg r
if length(r)>70 then do
p=lastpos(',',r,70)
parse var r f =(p) +1 r
queue f','
queue '// 'r
end
else queue r
return 0 |
|
|
| Back to top |
|
 |
sergeyken
Senior Member

Joined: 29 Apr 2008 Posts: 2264 Location: USA
|
|
|
|
| Willy Jensen wrote: |
"Since no reaction received," Ok, I'll bite. Here is my suggestion.
It may not cross all the 'T's, but works for the sample given.
Data is read using my own program, RXExecIo, just substitute with whatever input means that you fancy. The generated records are just show, again replace with the write feature that you like. |
One thing is critical: the call to Subst() must go before call to GetSet(), because some previously defined variables may be used in the SET statement itself. |
|
| Back to top |
|
 |
Willy Jensen
Active Member

Joined: 01 Sep 2015 Posts: 772 Location: Denmark
|
|
|
|
| Good point. |
|
| Back to top |
|
 |
Willy Jensen
Active Member

Joined: 01 Sep 2015 Posts: 772 Location: Denmark
|
|
|
|
Slightly modified pgm:
| Code: |
set.=''
"delstack"
Call RXExecIo 'get dsn(TEST.SEQ.F080) stem(jcl.)' /* my READ pgm */
say 'JCL records read:' 0+jcl.0
do j#=1 to jcl.0 /* over JCL input */
jclrec=jcl.j#
if left(jclrec,2)='//' & substr(jclrec,3,1)<>'*' then do
if word(jclrec,2)='JOB' then do
say 'New job' substr(word(jclrec,1),3)
set.=''
end
if pos('&',jclrec)>0 then jclrec=Subst(strip(left(jclrec,72)))
if word(jclrec,2)='SET' then jclrec=GetSet(strip(left(jclrec,72)))
if length(jclrec)>70 then do /* long line */
p=lastpos(',',jclrec,70)
parse var jclrec f =(p) +1 jclrec
queue f','
queue '// 'jclrec
end
else queue jclrec
end
else queue jclrec
end
say ''
say 'Generated...'
do queued()
parse pull r
say r
end
"delstack" /* not really neccessary */
Exit 0
GetSet: /* locate var defs, save values */
parse value arg(1) with r . 'SET' n . '=' v
if left(v,1)="'" then parse var v =2 v "'" .
else parse var v v .
set.n=v
return insert('*',arg(1),2) /* make SET a coment */
Subst: /* locate variables in JCL, replace with stored value */
ir=arg(1)' '
say 'subst in: ' arg(1)
or=''
do forever
p=pos('&',ir)
if p=0 then leave
if substr(ir,p+1,1)='&' then do /* temp dsn */
or=or'&&'
ir=substr(ir,3)
iterate
end
parse var ir f '&' ir /* get front, name and back */
or=or || f
p=Verify(ir,'.,() ','m') /* pos at first of list */
parse var ir v =(p) ir
if left(ir,2)='..' then ir=substr(ir,2)
if set.v<>'' then v=set.v
else do
say '*' v 'is not SET'
v='?'v'?'
end
or=or || v
end
or=or || ir /* add whats left */
say 'subst out:' or
return or |
|
|
| Back to top |
|
 |
sergeyken
Senior Member

Joined: 29 Apr 2008 Posts: 2264 Location: USA
|
|
|
|
Generated:
| Code: |
//DBPTEMP JOB (CS-P-CG-CAD-0),'DBA-DB2-JOB',CLASS=C,
//
//** RESTART=STEP####, <<< REMOVE * AND POINT TO RESTART STEP
// MSGCLASS=N,MSGLEVEL=(1,1),REGION=0M ,
// PERFORM=216
//* |
Must be:
| Code: |
//DBPTEMP JOB (CS-P-CG-CAD-0),'DBA-DB2-JOB',CLASS=C,
//** RESTART=STEP####, <<< REMOVE * AND POINT TO RESTART STEP
// MSGCLASS=N,MSGLEVEL=(1,1),REGION=0M ,PERFORM=216
//* |
Generated:
| Code: |
//PROCSTMT JCLLIB ORDER=(DBP.SUP.DBA.PROC,
//
// CCP.BASE.PROCLIB)
// / CCP.BASE.PROCLIB)
|
Must be:
| Code: |
//PROCSTMT JCLLIB ORDER=(DBP.SUP.DBA.PROC,
// CCP.BASE.PROCLIB)
|
Generated:
| Code: |
//* SET PREFC='4'
//* SET FC='?PREFC'? << FUTURE CHECK - TODAY + 4 DAYS |
Must be:
| Code: |
// SET PREFC='4'
// SET FC='4' << FUTURE CHECK - TODAY + 4 DAYS |
|
|
| Back to top |
|
 |
Willy Jensen
Active Member

Joined: 01 Sep 2015 Posts: 772 Location: Denmark
|
|
|
|
I admit to be confused. When I run the originators JCL through my latest code I get:
| Code: |
Generated...
//DBPTEMP JOB (CS-P-CG-CAD-0),'DBA-DB2-JOB',CLASS=C,
//** RESTART=STEP####, <<< REMOVE * AND POINT TO RESTART STEP
// MSGCLASS=N,MSGLEVEL=(1,1),REGION=0M ,PERFORM=216
//*
//*********************************************************************
//* RESTART INSTRUCTION :
//* DO NOT RESTART IN CASE OF ABENDS, CONTACT DB2 DBA ONCALL
//*
//*********************************************************************
//* THIS JOB BUILDS
//*********************************************************************
//*
//PROCSTMT JCLLIB ORDER=(DBP.SUP.DBA.PROC,
// CCP.BASE.PROCLIB)
//*
// EXPORT SYMLIST=(DB2SS,EPFIX,SI,JBNM)
//* SET SHLQ='SYSGA' <<HLQ OF SYSTEM FILES - LPAR DEPENDENT
//* SET DB2SS='A' <<SET DB2 SUBSYSTEM
//* SET SI='A0P' <<SCHEMA IDENTIFIER
//* SET EPFIX='DBQ' <<ENVIRONMENT LEVEL
//* SET JBNM='D2PDR00' <<JOBNAME FOR EMAIL STEPS
//* SET RNDT='YYYY-MM-DD' << RUN DATE, INVALID DATES USE CURRENT DATE
//* << USE TO TEST A SPECIFIC DATE
//* SET LRC=' ' << FORCE LIST OF REORG_CONTROL BPOOL REPORT
//* << DDNAME IS BPCOLLFL IF THE VALUE IS 'R'
//* SET TF=' ' << TRACE FLAG - FOR TESTING THE PROGRAM
//* SET FC='4' << FUTURE CHECK - TODAY + 4 DAYS
//* SET CHKPGM=D6REOCHK << NEW VERSION STORED PROC REBINDS 04/23/24
//* PARM='&RNDT.&SI.&FC.&LRC.&TF.'
//**
//JOBLIB DD DISP=SHR,DSN=SYSGA.DB2A.SDSNEXIT
// DD DISP=SHR,DSN=SYSGA.DB2A.SDSNLOAD |
Which looks ok to me. The blank line is also in the original. Then you mention SET PREFC='4' , which I cannot find anywhere in that original JCL sample. So what am I missing (possible apart from new glasses)?
You wish to retain the SET statements, no poblem, but I would have thought it better to dummy them out as that should result in JCL errors for any mistakingly left uses of the variables. Each to his/her own. |
|
| Back to top |
|
 |
Willy Jensen
Active Member

Joined: 01 Sep 2015 Posts: 772 Location: Denmark
|
|
|
|
Just noticed the EXPORT SYMLIST statement, which indicates that at least some SET values may be used in SYSIN data, so yes the SET statements should be left unmodified. And the statement
should instead be
|
|
| Back to top |
|
 |
sergeyken
Senior Member

Joined: 29 Apr 2008 Posts: 2264 Location: USA
|
|
|
|
OK, no problem.
I started this topic just for fun. |
|
| Back to top |
|
 |
Willy Jensen
Active Member

Joined: 01 Sep 2015 Posts: 772 Location: Denmark
|
|
| Back to top |
|
 |
|
|
 |
All times are GMT + 6 Hours |
|