Using COND in JCL on both JOB and EXEC - mainframe

I am trying to get my head around using the COND statement in JCL on both the JOB step and the EXEC step. My aim is as follows:
All steps must complete with zero return code
Unless explicitly indicated for a particular step
The job should stop when a step completes with a return code not expected
Mostly, all steps will complete with zero, so it is the unusual path to have the over-ride. I don't want to have to code COND on each EXEC step to cover the normal zero case.
I had hoped that the following would do this, but I think the priority of COND on the JOB step appears to over-ride COND on an EXEC step.
//MYJOB JOB ,COND=(0,NE)
//JOBLIB DD DSN=...
// DD DSN=...
//STEP1 EXEC PGM=MYPGM1
//STEP2 EXEC PGM=MYPGM2
//STEP3 EXEC PGM=MYPGM3,COND=(8,NE,STEP2)
//STEP4 EXEC PGM=MYPGM4
//
Is there any way to code this without doing the following:
//MYJOB JOB
//JOBLIB DD DSN=...
// DD DSN=...
//STEP1 EXEC PGM=MYPGM1
//STEP2 EXEC PGM=MYPGM2,COND=(0,NE,STEP1)
//STEP3 EXEC PGM=MYPGM3,COND=(8,NE,STEP2)
//STEP4 EXEC PGM=MYPGM4,COND=(0,NE,STEP3)
//

According to the JCL Language Reference manual the system first checks the condition specified on the job card. If this is met the job terminates otherwise it then checks the cond code on the step.
FWIW I have always seen step condition codes and not JOB condition codes.

Going by your dot pointed criteria your options are to code step based CONDs (which you have already discerned) or to use IF...THEN...ELSE...ENDIF statements around blocks of steps that you want to prevent from running based on return codes. IFs are much easier to code and read than CONDs although they are more verbose.
For example, this job uses IDCAMS to force a return code via the SET LASTCC command. The underscore is an integer up to 16, any higher will simply yield 16.
I have wrapped steps in 2 IF...ENDIF blocks. See below for output using RCs that trigger each outcome.
Command ===> Scroll ===> CSR
000001 //MYJOB JOB (AA-1234),MYJOB,CLASS=P,MSGCLASS=X,REGION=0M,
000002 // NOTIFY=&SYSUID
000003 //*
000004 //STEP1 EXEC PGM=IDCAMS
000005 //SYSPRINT DD SYSOUT=*
000006 //SYSIN DD *
000007 SET LASTCC=_
000008 /*
000009 //IF1 IF RC EQ 0 THEN
000010 //STEP2 EXEC PGM=IDCAMS
000011 //SYSPRINT DD SYSOUT=*
000012 //SYSIN DD *
000013 SET LASTCC=_
000014 /*
000015 //IF2 IF STEP2.RC LT 8 THEN
000016 //STEP3 EXEC PGM=IEFBR14
000017 //STEP4 EXEC PGM=IEFBR14
000018 //STEP5 EXEC PGM=IEFBR14
000019 //EIF2 ENDIF
000020 //EIF1 ENDIF
****** ******************************** Bottom of Data ********************************
Will yield the following:
When I set the LASTCC value of STEP1 to 4 and STEP2 to any :
14.03.41 JOB26149 IEF403I MYJOB - STARTED - TIME=14.03.41
14.03.41 JOB26149 GSDMV20I -JOBNAME STEPNAME PROCSTEP CCODE ELAPSED-TIME CPU-TIME STEPNO
14.03.41 JOB26149 GSDMV21I -MYJOB STEP1 4 00:00:00 0.02S 1
14.03.41 JOB26149 GSDMV21I -MYJOB STEP2 FLUSH 00:00:00 0.00S 2
14.03.41 JOB26149 GSDMV21I -MYJOB STEP3 FLUSH 00:00:00 0.00S 3
14.03.41 JOB26149 GSDMV21I -MYJOB STEP4 FLUSH 00:00:00 0.00S 4
14.03.41 JOB26149 GSDMV21I -MYJOB STEP5 FLUSH 00:00:00 0.00S 5
14.03.41 JOB26149 IEF404I MYJOB - ENDED - TIME=14.03.41
14.03.41 JOB26149 $HASP395 MYJOB ENDED - RC=0004
When I set the the LASTCC value of STEP1 to 0 and STEP2 to 4 :
14.02.40 JOB26136 IEF403I MYJOB - STARTED - TIME=14.02.40
14.02.40 JOB26136 GSDMV20I -JOBNAME STEPNAME PROCSTEP CCODE ELAPSED-TIME CPU-TIME STEPNO
14.02.40 JOB26136 GSDMV21I -MYJOB STEP1 0 00:00:00 0.03S 1
14.02.41 JOB26136 GSDMV21I -MYJOB STEP2 4 00:00:00 0.02S 2
14.02.41 JOB26136 GSDMV21I -MYJOB STEP3 0 00:00:00 0.00S 3
14.02.41 JOB26136 GSDMV21I -MYJOB STEP4 0 00:00:00 0.00S 4
14.02.41 JOB26136 GSDMV21I -MYJOB STEP5 0 00:00:00 0.00S 5
14.02.41 JOB26136 IEF404I MYJOB - ENDED - TIME=14.02.41
14.02.41 JOB26136 $HASP395 MYJOB ENDED - RC=0004
When I set the the LASTCC value of STEP1 to 0 and STEP2 to 8 :
13.59.41 JOB25747 IEF403I MYJOB - STARTED - TIME=13.59.41
13.59.41 JOB25747 GSDMV20I -JOBNAME STEPNAME PROCSTEP CCODE ELAPSED-TIME CPU-TIME STEPNO
13.59.41 JOB25747 GSDMV21I -MYJOB STEP1 0 00:00:00 0.03S 1
13.59.41 JOB25747 GSDMV21I -MYJOB STEP2 8 00:00:00 0.01S 2
13.59.41 JOB25747 GSDMV21I -MYJOB STEP3 FLUSH 00:00:00 0.00S 3
13.59.41 JOB25747 GSDMV21I -MYJOB STEP4 FLUSH 00:00:00 0.00S 4
13.59.41 JOB25747 GSDMV21I -MYJOB STEP5 FLUSH 00:00:00 0.00S 5
13.59.41 JOB25747 IEF404I MYJOB - ENDED - TIME=13.59.41
13.59.41 JOB25747 $HASP395 MYJOB ENDED - RC=0008
Hope this helps in some way.

You're asking if there is any way to accmomplish what you want. Here is one using an instream procedure:
//jobname JOB .....
//*
//* --- Start of instream procedure -----------------------------------
//*
//$EXEC PROC $PGM=,$PARM=,$COND=(0,NE)
//PS EXEC PGM=&$PGM,PARM=&$PARM,COND=(&$COND)
// PEND
//*
//* --- End of instream procedure -------------------------------------
//*
//STEP01 EXEC $EXEC,$PGM=MYPGM1
//* add any DD statements for MYPGM1 hereafter
//*
//STEP02 EXEC $EXEC,$PGM=MYPGM2
//* add any DD statements for MYPGM2 hereafter
//*
//STEP03 EXEC $EXEC,$PGM=MYPGM3,$COND=(8,NE,STEP02.PS)
//* add any DD statements for MYPGM3 hereafter
//*
//STEP04 EXEC $EXEC,$PGM=MYPGM4
//* add any DD statements for MYPGM4 hereafter
//*
The instream prodecure has COND=(0,NE) on the EXEC, so this applies to any step executing that procedure. Using the $COND procedure parameter, you can override on any step as needed. Note that the actual step is now a procedure step, so COND= needs you to specify the step ("STEPnn" here), and the procedure step (always "PS" in this sample).

Related

Selectively switch bash script on and off from inside another bash script

I have a Raspberry Pi running a bash script, eg, ScriptA, that calls a web function running on a cloud server and the function returns a value to the calling script. The cloud server contains a system where, visitors to the site are given a few options using buttons, eg, Button1, Button2 and Button3. The option selected by the visitor is sent by the web function to the calling bash script on the Pi as Request1, Request2 or Request3 for Button1, Button and Button3 respectively. The ScriptA keeps calling the web function recursively to check for any change in Request value. The value returned by the function to the Pi is then used to run another script on the Pi, eg, Request1 runs Script1, Request2 runs Script2 and Request3 runs Script3. Now, the webpage is dynamic and the visitor can click different options, one after the other, and the script running on the Pi also needs to change accordingly.
Button1 => Request1 |                           | Request1 => Script1
Button2 => Request2 | Cloud Server <=> Pi(ScriptA) | Request2 => Script2
Button3 => Request3 |                           | Request3 => Script3
What I have done is, I have used a switch case in the ScriptA to switch between the different scripts 1, 2 and 3. What I want is, as soon as the visitor to the site chooses a different option and the Pi receives the new request, the script for the previously selected option stops executing, and the script for the new request starts executing.
#!/bin/bash
# call.sh
while:
do
req=$(curl -d "param=$param" http://www.example.net/req.php)
case $req in
*req1*)
sudo sh /home/pi/stopscript2.sh
sudo sh /home/pi/stopscript3.sh
sudo sh /home/pi/startscript1.sh
;;
*req2*)
sudo sh /home/pi/stopscript1.sh
sudo sh /home/pi/stopscript3.sh
sudo sh /home/pi/startscript2.sh
;;
*req3*)
sudo sh /home/pi/stopscript1.sh
sudo sh /home/pi/stopscript2.sh
sudo sh /home/pi/startscript3.sh
;;
esac
done
But, the problem with this piece of code is that, the first time the pi receives a request and starts executing the specific script for the incoming request, no more calls to the web function are made thereafter and the script continues executing the first request, regardless of whether the user has selected a new option or not.
How do I make it work as desired? I hope I could frame my question well enough.
Help is much welcome. Thanks in advance.
Assuming that:
we keep reading the new input and just execute the freshly gathered
value, whether "Request1", "Reqest2" or "Request3" and get rid of
the old one.
You don't mind previous process to be killed rather than to be stopped.
In addition to your approach, I have added the following:
A date,time print function, to demonstrate operation timing clearly. This can be omitted later one once functionality has been verified.
A 'getpid' function, which freshly calculates the pid of the previous running script and passes on to the kill function
To demonstrate this, I will be tailing a file data.txt which is continuously growing as 3 backgroud echo commands are continuously putting Request 1,2,3 into it. This will serve as our somewhat dynamic random input. A sample below.
Sample:
%_STATION#gaurav * /root/ga/shell> tail -f data.txt
Request2
Request1
Request1
Request1
Request3
Request2
Request1
The script output:
The line with EMPTY represents when no PID was found. EMPTY
This indicates a new request read from your file: [17-02-17 20:38:17|[>>>>NEW>>>REQUEST>> [Request3]>>>>]
[gaurav 82577 1 0 20:38 pts/3 00:00:00 sleep 11]. This is the old running process, so I will kill it now.
Its PID is captured: [OLDPID---->82577]
Once killed, a new one fired, and it is "Request3": [gaurav 82611 1 0 20:38 pts/3 00:00:00 sleep 13]
Now this keeps repeating. See the log below for more clarity.
A log excerpt:
17-02-17 20:38:16|EMPTY
17-02-17 20:38:16|[***NEWPROCESS***[82504]*****]
17-02-17 20:38:16|[>>>>NEW>>>REQUEST>> [Request1]>>>>]
17-02-17 20:38:16|gaurav 82504 1 0 20:38 pts/3 00:00:00 sleep 12
17-02-17 20:38:16|[OLDPID---->82504]
17-02-17 20:38:16|gaurav 82543 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:16|[***NEWPROCESS***[82543]*****]
17-02-17 20:38:16|[>>>>NEW>>>REQUEST>> [Request1]>>>>]
17-02-17 20:38:16|gaurav 82543 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:16|[OLDPID---->82543]
17-02-17 20:38:17|gaurav 82577 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:17|[***NEWPROCESS***[82577]*****]
17-02-17 20:38:17|[>>>>NEW>>>REQUEST>> [Request3]>>>>]
17-02-17 20:38:17|gaurav 82577 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:17|[OLDPID---->82577]
17-02-17 20:38:17|gaurav 82611 1 0 20:38 pts/3 00:00:00 sleep 13
17-02-17 20:38:17|[***NEWPROCESS***[82611]*****]
17-02-17 20:38:17|[>>>>NEW>>>REQUEST>> [Request2]>>>>]
17-02-17 20:38:17|gaurav 82611 1 0 20:38 pts/3 00:00:00 sleep 13
17-02-17 20:38:17|[OLDPID---->82611]
17-02-17 20:38:17|EMPTY
17-02-17 20:38:17|[***NEWPROCESS***[82664]*****]
17-02-17 20:38:17|[>>>>NEW>>>REQUEST>> [Request1]>>>>]
17-02-17 20:38:17|gaurav 82664 1 0 20:38 pts/3 00:00:00 sleep 12
17-02-17 20:38:17|[OLDPID---->82664]
17-02-17 20:38:17|gaurav 82699 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:17|[***NEWPROCESS***[82699]*****]
17-02-17 20:38:17|[>>>>NEW>>>REQUEST>> [Request1]>>>>]
17-02-17 20:38:17|gaurav 82699 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:17|[OLDPID---->82699]
17-02-17 20:38:18|gaurav 82733 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:18|[***NEWPROCESS***[82733]*****]
17-02-17 20:38:18|[>>>>NEW>>>REQUEST>> [Request3]>>>>]
17-02-17 20:38:18|gaurav 82733 1 0 20:38 pts/3 00:00:00 sleep 11
17-02-17 20:38:18|[OLDPID---->82733]
17-02-17 20:38:18|gaurav 82767 1 0 20:38 pts/3 00:00:00 sleep 13
17-02-17 20:38:18|[***NEWPROCESS***[82767]*****]
17-02-17 20:38:18|[>>>>NEW>>>REQUEST>> [Request2]>>>>]
As seen in the above log, the script is now able to act according to the incoming requests. In your case, you can replace the tail -f data.txt | while read line line portion with while true; do and then put your curl command.
The script:
%_STATION#gaurav * /root/ga/shell> cat request_action.sh
#!/bin/bash
# configure your scirpts. Here I used Sleeps.
s1='sleep 11' ; s2='sleep 12' ; s3='sleep 13'
# DateTime and print function.
prnt()
{
str="$1"
dt=$(date +"%d-%m-%y "%T)
[[ -z "$str" ]] && str="EMPTY"
echo "$dt|$str"
}
# Function to generate PID of previous running script.
getpid()
{
pid=$(ps -ef|egrep "$s1|$s2|$s3"|grep -v grep|awk '{print $2}')
echo "$pid"
}
# To kill the existing running script/s.
stopnstart()
{
prnt "$(ps -ef|egrep "$s1|$s2|$s3"|grep -v grep)"
prnt "[OLDPID---->$(getpid)]"
$(kill -9 $(getpid) 1>/dev/null 2>&1) # Kill the old PID.
$($1 1>/dev/null 2>&1 &) # Now run the fresh script.
prnt "$(ps -ef|egrep "$s1|$s1|$s3"|grep -v grep)"
prnt "[***NEWPROCESS***[$(getpid)]*****]"
}
# Main loop
tail -f data.txt | while read line
do
prnt "[>>>>NEW>>>REQUEST>> [$line]>>>>]"
case $line in
Request1)
stopnstart "$s1"
;;
Request2)
stopnstart "$s2"
;;
Request3)
stopnstart "$s3"
;;
esac
done
%_STATION#gaurav * /root/ga/shell>
I hope I got it right. Thanks.

Self duplicate background script

This is a background script test.
When run it launch two processes and I don't understand why.
One stop after sleep 20. And other forgets.
#!/bin/bash
back(){
n=0
while [ 1 ]
do
echo $n
n=$(($n+1))
sleep 5
done
}
back &
sleep 20
exit
command "ps -a" in call:
PID TTY TIME CMD
8964 pts/2 00:00:00 backgroundtest
8965 pts/2 00:00:00 backgroundtest
8966 pts/2 00:00:00 sleep
8982 pts/2 00:00:00 sleep
after sleep 20:
PID TTY TIME CMD
8965 pts/2 00:00:00 backgroundtest
9268 pts/2 00:00:00 sleep
then run forever...
why?
while [ 1 ] is an infinite loop. [ 1 ] is always true.
So back & is an infinite loop, started in background (&), then execution continues with sleep 20, which does end after 20 seconds, leaving you with two processes for 20 seconds (& starts a new process in background), then the infinite one after that.

JCL what is wrong with this?

I am trying to crack this JCL and wonder what is wrong.
This is my code :
000001 //SORTJCL JOB
000002 //SORTSTEP EXEC PGM=SORT
000003 //SYSOUT DD SYSOUT=*
000004 //SORTOUT DD SYSOUT=*
000005 //SORTWK01 DD SPACE=(CYL,(1,1))
000006 //SORTIN DD DISP=SHR,DSN=Y2015.PUBLIC.DATA(AREACODE)
000007 //SYSIN DD *
000008 SORT FIELDS=(6,10,CH,A)
000009 // IF RC = 0 THEN
000010 //COPYSTEP EXEC PGM=ICEGENER
000011 //SYSUT1 DD DISP=SHR,DSN=Y2015.PUBLIC.DATA($005)
000012 //SYSUT2 DD DISP=SHR,DSN=&SYSUID..P2.OUTPUT($005)
000013 //SYSOUT DD SYSOUT=*
000014 //SYSPRINT DD SYSOUT=*
000015 //SYSIN DD DUMMY
000016 // ELSE
000017 // ENDIF
The purpose of this code - to read and sort Y2015.PUBLIC.DATA(AREACODE) and copy and write the output into MYID.P2.OUTPUT($005)
Can anyone explain to me what am I missing?
To "read and sort Y2015.PUBLIC.DATA(AREACODE)", you already use the correct //SORTIN in your first jobstep, in line 000006. The result of that read and sort gets written to //SORTOUT, which in your JCL seems to be written to SYSOUT=* (the spool), in line 000003.
But that's NOT what you should do (as per your "copy and write the output into MYID.P2.OUTPUT($005)"). So therefor you must modify that line 000003 like so:
000003 //SYSOUT DD DISP=SHR,DSN=&SYSUID..P2.OUTPUT($005)
After you applied this change, you also have to remove everything related to the 2nd jobstep (so starting from line 000009 and everything following that line).
If for whatever reason you still want all those lines to continue to be included in this JCL, just insert a new line in front of line 000009 that looks like so:
000009 //
This will cause all remaining JCL lines following it to just be ignored.

time command output on an already running process

I have a process that spawns some other processes,
I want to use the time command on a specific process and get the same output as the time command.
Is that possible and how?
I want to use the time command on a specific process and get the same output as the time command.
Probably it is enough just to use pidstat to get user and sys time:
$ pidstat -p 30122 1 4
Linux 2.6.32-431.el6.x86_64 (hostname) 05/15/2014 _x86_64_ (8 CPU)
04:42:28 PM PID %usr %system %guest %CPU CPU Command
04:42:29 PM 30122 706.00 16.00 0.00 722.00 3 has_serverd
04:42:30 PM 30122 714.00 12.00 0.00 726.00 3 has_serverd
04:42:31 PM 30122 714.00 14.00 0.00 728.00 3 has_serverd
04:42:32 PM 30122 708.00 16.00 0.00 724.00 3 has_serverd
Average: 30122 710.50 14.50 0.00 725.00 - has_serverd
If not then according to strace time uses wait4 system call (http://linux.die.net/man/2/wait4) to get information about a process from the kernel. The same info returns getrusage but you cannot call it for an arbitrary process according to its documentation (http://linux.die.net/man/2/getrusage).
So, I do not know any command that will give the same output. However it is feasible to create a bash script that gets PID of the specific process and outputs something like time outpus then
This script does these steps:
1) Get the number of clock ticks per second
getconf CLK_TCK
I assume it is 100 and 1 tick is equal to 10 milliseconds.
2) Then in loop do the same sequence of commands while exists the directory /proc/YOUR-PID:
while [ -e "/proc/YOUR-PID" ];
do
read USER_TIME SYS_TIME REAL_TIME <<< $(cat /proc/PID/stat | awk '{print $14, $15, $22;}')
sleep 0.1
end loop
Some explanation - according to man proc :
user time: ($14) - utime - Amount of time that this process has been scheduled in user mode, measured in clock ticks
sys time: ($15) - stime - Amount of time that this process has been scheduled in kernel mode, measured in clock ticks
starttime ($22) - The time in jiffies the process started after system boot.
3) When the process is finished get finish time
read FINISH_TIME <<< $(cat '/proc/self/stat' | awk '{print $22;}')
And then output:
the real time = ($FINISH_TIME-$REAL_TIME) * 10 - in milliseconds
user time: ($USER_TIME/(getconf CLK_TCK)) * 10 - in milliseconds
sys time: ($SYS_TIME/(getconf CLK_TCK)) * 10 - in milliseconds
I think it should give roughly the same result as time. One possible problem I see is if the process exists for a very short period of time.
This is my implementation of time:
#!/bin/bash
# Uses herestrings
print_res_jeffies()
{
let "TIME_M=$2/60000"
let "TIME_S=($2-$TIME_M*60000)/1000"
let "TIME_MS=$2-$TIME_M*60000-$TIME_S*1000"
printf "%s\t%dm%d.%03dms\n" $1 $TIME_M $TIME_S $TIME_MS
}
print_res_ticks()
{
let "TIME_M=$2/6000"
let "TIME_S=($2-$TIME_M*6000)/100"
let "TIME_MS=($2-$TIME_M*6000-$TIME_S*100)*10"
printf "%s\t%dm%d.%03dms\n" $1 $TIME_M $TIME_S $TIME_MS
}
if [ $(getconf CLK_TCK) != 100 ]; then
exit 1;
fi
if [ $# != 1 ]; then
exit 1;
fi
PROC_DIR="/proc/"$1
if [ ! -e $PROC_DIR ]; then
exit 1
fi
USER_TIME=0
SYS_TIME=0
START_TIME=0
while [ -e $PROC_DIR ]; do
read TMP_USER_TIME TMP_SYS_TIME TMP_START_TIME <<< $(cat $PROC_DIR/stat | awk '{print $14, $15, $22;}')
if [ -e $PROC_DIR ]; then
USER_TIME=$TMP_USER_TIME
SYS_TIME=$TMP_SYS_TIME
START_TIME=$TMP_START_TIME
sleep 0.1
else
break
fi
done
read FINISH_TIME <<< $(cat '/proc/self/stat' | awk '{print $22;}')
let "REAL_TIME=($FINISH_TIME - $START_TIME)*10"
print_res_jeffies 'real' $REAL_TIME
print_res_ticks 'user' $USER_TIME
print_res_ticks 'sys' $SYS_TIME
And this is an example that compares my implementation of time and real time:
>time ./sys_intensive > /dev/null
Alarm clock
real 0m10.004s
user 0m9.883s
sys 0m0.034s
In another terminal window I run my_time.sh and give it PID:
>./my_time.sh `pidof sys_intensive`
real 0m10.010ms
user 0m9.780ms
sys 0m0.030ms

How to delete 2 GDG member in different steps

I defined a Generation Data Group (GDG) with limit parameter as 5, and let's put (1,2,3,4,5) as members(suppose 5 is current position).
I use a job which has 2 steps each will try to delete a member using IEFBR14 utility.
//STEP10 EXEC PGM=IEFBR14
//SYSOUT DD SYSOUT=*
//SYSDEL DD DSN=DATA.TEST.GDG(-1),
// DISP=(MOD,DELETE,DELETE)
//****************************************
//STEP20 EXEC PGM=IEFBR14
//SYSOUT DD SYSOUT=*
//SYSDEL DD DSN=DATA.TEST.GDG(-2),
// DISP=(MOD,DELETE,DELETE)
I wish I can get result as (1,2,5), but in fact (1,3,5) was left, member 2 and member 4 was deleted ? it seems after step 1, there is a commit operation, can anybody can help me with this?
But on the other hand, if I try to delete member (0), and member (-2), the result is as what I expected.
//STEP10 EXEC PGM=IEFBR14
//SYSOUT DD SYSOUT=*
//SYSDEL DD DSN=DATA.TEST.GDG(0),
// DISP=(MOD,DELETE,DELETE)
//****************************************
//STEP20 EXEC PGM=IEFBR14
//SYSOUT DD SYSOUT=*
//SYSDEL DD DSN=DATA.TEST.GDG(-2),
// DISP=(MOD,DELETE,DELETE)
I get the result (1,2,4), member 3 and member 5 were deleted.
JOB1 deletes the Members 2 and 4. Here is how it works.
enter code here
//STEP1 EXEC PGM=IEFBR14
//DD1 DD DSN=DATA.GDG.TEST(-1),DISP=(MOD,DELETE,DELETE)
//*
//STEP2 EXEC PGM=SORT <==note:i added this step for TEST purpose only
//SORTIN DD DSN=DATA.GDG.TEST(-1),DISP=SHR
//SORTOUT DD DUMMY
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
OPTION COPY
/*
//STEP3 EXEC PGM=IEFBR14
//DD2 DD DSN=DATA.GDG.TEST(-2),DISP=(MOD,DELETE,DELETE)
if we have a look at the JESYMSG, we can find the messages as below:
IEF142I GDGTST STEP1 - STEP WAS EXECUTED - COND CODE 0000
IGD105I DATA.GDG.TEST.G0004V00 DELETED, DDNAME=DD1 <==(-1) to 5
**please note here in STEP2 the GDG member refered is 03 not 04 as expected**
IEF142I GDGTST STEP2 - STEP WAS EXECUTED - COND CODE 0000
IGD104I DATA.GDG.TEST.G0003V00 RETAINED, DDNAME=SORTIN <==(-1) to 4
IEF142I GDGTST STEP3 - STEP WAS EXECUTED - COND CODE 0000
IGD105I DATA.GDG.TEST.G0002V00 DELETED, DDNAME=DD2 <==(-2) to 4
The first time you use a relative generation number for a generation data group within a job, the system establishes the relationship between the relative generation number and the absolute generation number. The system maintains this relationship throughout the job.(reference: z/OS MVS JCL user's guide-APPENDIX B)
For example, if you create a generation data set with a relative generation number of (+1), the system recognizes any subsequent reference to (+1) throughout the job as having the same absolute generation number.
(+1)--> adding a member raltive to most recently added member(i.e.,(0))
(+2)-->(+1) to (+1)
(+3)--> (+1) to (+2)
likewise, in the JOB1 that you posted the reference is set to (-1) i.e, G0004V00 at the starting of the job. This (-1) relationship is maintained throughout the job. result of STEP2 in the job psosted by me proves this point.
so to achieve the result (1,3,5) give (-1) in both the steps(STEP10 and STEP20) of JOB1. that will work.
above results are for JES2, not sure about JES3
and for JOB2 in Main question:
//STEP1 EXEC PGM=IEFBR14
//DD1 DD DSN=DATA.GDG.TEST(0),DISP=(MOD,DELETE,DELETE)
//SYSOUT DD SYSOUT=*
//STEP2 EXEC PGM=SORT
//SORTIN DD DSN=DATA.GDG.TEST(-1),DISP=SHR
//SORTOUT DD DUMMY
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
OPTION COPY
/*
//STEP3 EXEC PGM=IEFBR14
//DD2 DD DSN=DATA.GDG.TEST(-2),DISP=(MOD,DELETE)
//SYSOUT DD SYSOUT=*
//STEP4 EXEC PGM=IEFBR14
//MODEL1 DD DSN=DATA.GDG.TEST(-1),DISP=(MOD,DELETE)
the JESYMSG is as below:
IEF142I GDGTST STEP1 - STEP WAS EXECUTED - COND CODE 0000
IGD105I DATA.GDG.TEST.G0005V00 DELETED, DDNAME=DD1
IEF142I GDGTST STEP2 - STEP WAS EXECUTED - COND CODE 0000
IGD104I DATA.GDG.TEST.G0004V00 RETAINED, DDNAME=SORTIN
IEF142I GDGTST STEP3 - STEP WAS EXECUTED - COND CODE 0000
IGD105I DATA.GDG.TEST.G0003V00 DELETED, DDNAME=DD2
IEF142I GDGTST STEP4 - STEP WAS EXECUTED - COND CODE 0000
IGD105I DATA.GDG.TEST.G0004V00 DELETED, DDNAME=MODEL1
as I have explained earlier, the relationship between absolute and relative GDG is established when the job encounters the RELATIVE referencing for the FIRST time. so here in this job, it encounters(0) for the first time. so when the initiator issues ENQ at each step, the generation numbers will be resolved with reference to (0) in JOB2.
if we Observe here it deleted(0,3,4)--> for(0),(-2),(-1) in STEP1, STEP3,STEP4. that means, it didn't commit after the execution of eachstep. Reference to (-1) in STEP4 is resolved in relation to (0), which is established in STEP1.
lets consider one more example as below: if I add one morestep STEP5 like below to above job
//STEP1 EXEC PGM=IEFBR14
//DD1 DD DSN=DATA.GDG.TEST(-0),DISP=(MOD,DELETE,DELETE)
//SYSOUT DD SYSOUT=*
//STEP2 EXEC PGM=SORT
//SORTIN DD DSN=DATA.GDG.TEST(-1),DISP=SHR
//SORTOUT DD DUMMY
//SYSOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
OPTION COPY
/*
//STEP3 EXEC PGM=IEFBR14
//DD2 DD DSN=DATA.GDG.TEST(-2),DISP=(MOD,DELETE,DELETE)
//SYSOUT DD SYSOUT=*
//STEP4 EXEC PGM=IEFBR14
//MODEL1 DD DSN=DATA.GDG.TEST(-2),DISP=(MOD,DELETE)
//STEP5 EXEC PGM=IEFBR14
//DD2 DD DSN=DATA.GDG.TEST(-1),DISP=(MOD,DELETE,DELETE)
//STEP6 EXEC PGM=IEFBR14
//DD2 DD DSN=DATA.GDG.TEST(-2),DISP=(MOD,DELETE,DELETE)
it deletes(0,3,2,4)--> because at the starting of STEP4 it encountered a Ambiguity, so it resolved the reference against the catologue. For STEP6, again it has ambiguity so again it tries to resolve the reference according to the catologue, now as there are not enough generation members present(as we have created only 5 members and deleted 4 already),it throws the message like below:
IEF286I GDGTST STEP6 DD2 - DISP FIELD INCOMPATIBLE WITH DSNAME
IEF272I GDGTST STEP6 - STEP WAS NOT EXECUTED.

Resources