Mr.White
Mr.White

Reputation: 31

Regex to match the last line of a JCL job card or the whole card

If any of you are familiar with mainframe JCL.
I'm trying to match the last line of the job card.

Basically the first line that starts with // and ends without a comma. In the example I need the 3rd line or up to the 3rd line matched.

I'm using Ansible's lineinfile to dynamically insert a route card after the job card.

For example:

//SPOOL1   JOB (UU999999999,1103),'Programmer',CLASS=0, <--- start of job card
//         REGION=0M,MSGCLASS=R,TIME=5, LINES=(999999,WARNING),
//         NOTIFY=&SYSUID  <--- end of job card
//STEPNAME EXEC PGM=BPXBATCH 
//STDERR   DD   SYSOUT=*
//STDOUT   DD   SYSOUT=*
//STDPARM  DD   *
SH cat /dev/urandom

So far I got this, which matches the start of // and anything after, but, I cant figure out the last part

^(\Q//\E(.)*)

Upvotes: 2

Views: 454

Answers (4)

Roger
Roger

Reputation: 1

For the general case this is tougher than you think because of comments allowed within the scope of the JOB card.

    //SPOOL1   JOB (UU999999999,1103),'Programmer',CLASS=0, <--- start of job card
    //         REGION=0M,MSGCLASS=R,TIME=5, LINES=(999999,WARNING),
    //         NOTIFY=&SYSUID  <--- end of job card

The strings you show:

  • <--- start of job card
  • LINES=(999999,WARNING),
  • <--- end of job card

are all valid as comments in JCL because they follow a space.

You can even have whole comment lines within the JOB card. For example:

//name    JOB (accounting info),'data capture ___',     
//*            TYPRUN=SCAN,                                               
//             NOTIFY=&SYSUID,                                            
//             CLASS=A,MSGCLASS=T,MSGLEVEL=(1,1),TIME=(5,00),             
//             REGION=5M  

So you're not necessarily looking for the first card that doesn't end in a comma unless you can restrict the JCL you're looking at.

Your JOB card starts with //name JOB and ends just before the next //name card. *** edit *** As was correctly pointed out, the JOB card could be followed by a card which does not require a name field, like // SET for example. See https://www.ibm.com/docs/en/zos/2.4.0?topic=statements-jcl-statement-fields *** end of edit ***

It starts with ^(\Q//\E)[A-Z0-9]+\s+\QJOB\E.+ and ends just before the next named card ^(\Q//\E)[A-Z0-9]+\s+

But I don't know regular expressions well enough to find the "just before" point to insert your new line. Hopefully someone else can add that.

Upvotes: 0

Bohemian
Bohemian

Reputation: 425348

To match the whole job card (in this case 3 lines):

(?sm)\A.*?\/\/[^*]((?!\/\*)[^\n])*[^,]$

See live demo.

Breaking this down:

  • (?sm)
    • s enables the DOTALL flag (meaning . matches new lines too)
    • m enables the MUTLILINE flag (meaning ^ and $ match start and end of lines
  • \A means start of input (so it only matches at the very start)
  • .*? means anything, but as little as possible
  • //[^*]
  • ((?!\/\*)[^\n])* means non-new lines, except the sequence /* (so don't match when a comment is put in line)
  • [^,] not a comma
  • $ end of line

In English: "match from the start until there's a non-comma at the end of a line that is not a comment, or does not end with a comment"

You would then replace with $0 (group zero is the entire match) followed by your injected content:

$0\\n*ROUTE statement

Upvotes: 1

cschneid
cschneid

Reputation: 10775

Parsing JCL in the general case is hard. As noted in the comments, the rules are full of caveats.

I have an ANTLR4 grammar for JCL, it's MIT licensed. Possibly of use. It reflects the beauty of JCL.

Upvotes: 2

β.εηοιτ.βε
β.εηοιτ.βε

Reputation: 39314

You can use a negative lookbehind for this: (?<!,).
But you'll also need to insert after the firstmatch and use backrefs.

Given the task:

- lineinfile:
    path: file.jcl
    regexp: '^(\/\/.*)(?<!,)$'
    line: "\\1\\n//*ROUTE statement"
    firstmatch: true
    backrefs: true

You would end up, from your example, with:

//SPOOL1   JOB (UU999999999,1103),'Programmer',CLASS=0,
//         REGION=0M,MSGCLASS=R,TIME=5, LINES=(999999,WARNING),
//         NOTIFY=&SYSUID
//*ROUTE statement
//STEPNAME EXEC PGM=BPXBATCH 
//STDERR   DD   SYSOUT=*
//STDOUT   DD   SYSOUT=*
//STDPARM  DD   *
SH cat /dev/urandom

Upvotes: 0

Related Questions