JCL Basic Concepts — Introduction to JCL
This is an introduction to IBM z/OS MVS JCL basic concepts. Yes, there are people who want that. If that’s not you, then there’s nothing to see here, move along. But if you ARE somebody who just wants a simple intro to basic JCL, here it is.
JCL is easier than it might look. Obvious once you know it, like so many things.
A Short History of JCL
Sort of like a short bio of a person. If you’re in a hurry, skip ahead to ”Basic JCL statements”.
Notice how a line length of 80 characters is used in MVS a lot?
Originally computers did not have screens. Later there was one screen for the main operation of the computer only. Screens caught on, and we take them for granted now, but before that people mostly used punch cards to enter their programs and data into the computer. Each card was 80 columns wide. Why 80? Probably because that was a standard line length on letter-size typewriter paper.
Yes, to write a letter or a paper of any kind in those days, a person would put a sheet of paper, like copier paper, into a mechanism that fed the sheet through the device on a roller, advancing one line at a time. The keyboard was used to type characters directly onto the paper. Those were called typewriters, and they pre-dated computers. At the end of each line you would hit the ”carriage return” to go down to the start of the next line. That was the model for the idea of the ENTER key. It was also the model for CRLF: Carriage Return + Line Feed.
Early computer input devices, called keypunch machines, simply substituted an 80-column piece of rectangular lightweight cardboard instead of paper: Each such card was the equivalent of one typewritten line on paper. Whenever you typed a character on the keyboard it caused some holes to be punched in the next available column on the card, according to a code that mapped various patterns of holes to the various letters, numbers and other symbols that together constituted the character set available on the keyboard. Hence a small stack of cards (called a card deck) was equivalent to a sheet of paper. There could be any number of cards in a deck, making it conceptually more like a parchment scroll than a sheet of paper.
The visual representation of the punched holes appeared on the top line of the card, looking like a printed line a person could read. Well, as long as the ”interpreter” function was active on the machine, the top line was printed.
Notice how even today 80-column files often have columns 73 through 80 reserved for line numbers?
Yes, people quickly noticed that if you dropped a deck of cards it wasn’t always easy to be sure you put them back in the proper order.
IBM therefore made it an option on the keypunch machines that they could be rigged up to put line numbers automatically into the last 8 columns of each card. Most of the time these columns at the far right were otherwise unused, so this seemed a natural choice, to put the numbers off to the side out of the way.
You could also rig it up to skip to a particular column, based on the idea of tabbing on a typewriter. Yes, this ability led naturally to the now quaint-seeming custom of expecting particular types of input to be in particular columns. At the time, that seemed like a simplification.
Column 72, the last column just before the default line number field in columns 73-80, came to be commonly used to indicate continuation of a logical line (like, say, a sentence in English) onto another card. The set of cards thus linked was called a statement. Since most statements were usually less than one line long, the word “card” was often used informally as a shorter synonym for the word “statement” (as in: “the Job card” or “a DD card”). Strictly speaking they are not synonyms – but now you know why people refer to “a Job card”.
Nomenclature: One character (a single column) is also called a byte (pronounced bite), at least in ordinary “EBCDIC” (ebb-suh-dick)
Eventually they used up all of the available combinations that would fit into one column – that is, 256 possible internal combinations on an IBM machine– and they needed to add more characters for things like the Japanese katakana character set.
For that they came up with another system called a “double byte character set” (dbcs), and that is used for representing the extended set of characters that can’t be represented in one byte.
For now, as a simplification, we’re just going to proceed as if one byte still represented one character, which in JCL is true. Also note that characters used in JCL are REQUIRED TO BE UPPERCASE unless they are within a quoted string. By quotes we mean single quotes, also called apostrophes – JCL doesn’t use the double-quote character. Okay, if you want to embed a single apostrophe within a quoted string, you do that by putting two consecutive apostrophes in that place in the string, and the system will compress the two into a single apostrophe when it processes the string. But the actual double-quote character is not used in JCL.
There were other machines besides key punch machines: Some to read files from the computer (or from a deck of punched cards) and copy the lines onto output punched cards and/or paper, and those were called Card punch machines and printers; Some to handle cards in other ways, for example sorting them. All of these were collectively known as Unit Record machines. They fell in with adding machines (EAM = Electric Adding Machine) and were considered office equipment.
(Think about it, IBM = International Business Machines. Computer-related equipment is, or at least was, office equipment.)
The next big step was for IBM to rig up typewriters – IBM Selectric typewriters – to accept long rolls of paper fan-folded into perforated sheets, and to rig those typewriters to input your typing directly into a computer connection. Hence was the first commonly used modern computer terminal born, the IBM model 2741. It had a switch on the side so you could convert it back and forth between a plain typewriter and a terminal; also it could be a printer. The lines you typed on the terminal were patterned on the design that had been developed for punched cards – so columns 73 through 80 were still designated as line number columns and 72 was still the continuation column (a convention that continues to this day).
So let’s say you’re living back then and you want to type some data and put it into a computer file. You might want the file to be on hard disk, or you might want it to be on a reel of magnetic tape that you could carry away with you. How do you tell the computer to do that?
JCL: Job Control Language. Also, “utility programs”, aka “utilities”. People often conflate these two things, but they aren’t the same really. JCL is the language that the computer reads directly from an input device (such as a card reader or a terminal). The utility programs are apps provided by IBM to do commonplace functions like copying a file. You use JCL to tell the system to run (execute) a utility program, just as you would use JCL to run any other program.
JCL needs a minimum of two statements.
Basic JCL statements
First is a JOB statement, which is like a header record: It is used to direct any results or system-generated error messages back to you, to convey any accounting information if applicable for charging, to specify limits such as a time limit, and in general to communicate to the system any information, restrictions, routing and so on that will be applicable to the entire piece of work that you want the system to do for you; That piece of work is called a job.
Each job executes (runs) one or more programs. Each program to be executed requires an EXEC statement. The EXEC statements follow after the JOB statement.
If a program requires input data or produces output data, then the EXEC statement for that program is followed by a DD (Data Definition) statement for each data file the program will use: One DD statement for each data file.
Because it usually contains unintuitive non-obvious information like account numbers, people traditionally have not much liked to create job statements, in general, and doing so seemed to be error-prone besides.
So, when punched cards were in use, the people in charge of a system often handed out pre-printed color-coded job cards tailored to each user of the system.
If you signed up for an account you might be handed a small deck of pre-printed job “cards” – one card per job statement – which were identical except for the Job Name. The Job Name was typically assigned in ascending sequence. Say your user name was SALLY1 (letters were required to be uppercase only, and mostly in JCL they still are); you might be given a deck of cards with the job name field preprinted as SALLY101 through SALLY199. If you used up the cards you could get another set.
Most places printed the job cards on a special color of card stock, such as pink, so they stood out from the beige card stock that was used for most purposes. Sometimes if there were different classes of jobs – say short jobs and long-running jobs – they might provide two different colors of job cards, one for each job class.
Now you know why they are so often called “job cards” rather than “job statements”.
To this day people usually just copy an old job statement when they create a new job, because that’s easier than trying to remember accounting information.
Fields on a JCL Statement
Columns 1 and 2 of a JCL card are called the ID field or the Delimiter field.
The system uses this to recognize that the card represents a JCL statement. This was important originally, when all the input was being read from punched cards, and the system had to separate the Job Control Language instructions from input program source statements and other input data.
Usually the ID field is just two slashes. These have to be in columns one and two exactly. If you have a statement with just the // in 1 and 2, and nothing else on the card, that is called a NULL statement and its main purpose is to terminate the job.
People used to be very careful to include a NULL statement at the end of each job to avoid any chance of pulling in somebody else’s JCL as part of their job — in case the next person who turned in a deck of JCL for processing had failed to include a job statement at the start. If your job ended with a NULL statement, any other cards that might be read in right after it would be flushed until another job card was found. If you did not have a NULL statement, then somebody else could piggyback their work onto yours using your same accounting information – meaning that if charges were applicable, you or your department would be charged, and if security authorization was required, your authorization would be used for their work.
Those days are pretty much gone, and if you’re submitting a job online then the system generally takes care of terminating the JCL after the last line you send. But if you have a NULL statement, the system will terminate your job right then and there.
The Name field starts in column 3, and your basic name field can be up to 8 columns wide. A complex statement name consists of two or more basic names each separated from the preceding one by a dot (aka full stop, period, decimal point or point). A job name, though, is always a simple name, limited to a maximum of 8 characters.
Eight is a special number in JCL, and sometimes in other IBM things as well. Why eight? I have never heard a satisfactory explanation for the choice. You’ll run into the length limitation of 8 over and over again, though, so get used to it. Many people credit the choice to the fact that many of the early designers were engineers and as such were not required to have extensive vocabularies in basic English, and they thought 8 letters was quite long enough for a word to be. That seems perhaps a bit unkind, but I do not have a better explanation to offer either.
After the Job statement, you have to provide an EXEC statement, which tells the system what you want it to do for you. If you want it to do several things, you can have several EXEC statements within the same job. Almost the only thing the computer is able to do for you is to execute a program, so you tell it what program to execute, and then the program does all the work – or rather, the program, when it executes, directs the computer what to do, with very explicit step by step instructions (called the program).
Besides executing the program(s) you tell it to run by way of the EXEC statements you supply, the one other thing the computer system does for you is to run invisible internal programs that are part of the system itself, and these do setup and cleanup tasks on behalf of your program(s). However, the system only does those setup and cleanup tasks as a byproduct of processing for some program that you specify on an EXEC statement (except that it also does some overall job initiation and job termination for the whole job).
The name field of an EXEC statement is called the “step name”. The running of each specified program is called a job step. If you have only one EXEC statement, your job has only one step. If you have a bunch of EXEC statements then your job has a bunch of steps. If you have zero EXEC statements then the job fails and you get the message “job has no steps”.
Unsurprisingly, the name field of a JOB statement is called the Job name, and the name field of a DD statement is called the ddname.
An EXEC statement looks somewhat like a JOB statement, but usually simpler. It has all the same FIELDS. The ID or delimiter field comes first as always, composed of a double slash // in columns one and two. The name field still starts in column 3 as always – However, unlike the Job Name on the JOB card, the step name is optional on the EXEC statement. You can leave it blank as long as you never need to refer to it anyplace else. In any case, the name field ends with the first blank space, wherever the first blank occurs, whether or not the name field is used.
You can have several blanks if you wish, but only one is required. You will see a lot of JCL that is lined up so that the next field after the name field starts in column 12, as it would if the name were 8 bytes long. That isn’t required. It is done to make it easier for the human eye to read the JCL.
After the blank that terminates the name field, you have the OPERATION field. The operation field defines the type of JCL statement. It says JOB for a JOB statement, EXEC for an EXEC statement, DD for a DD statement and so on. Other examples are SET, INCLUDE, PROC, PEND, IF THEN, ELSE, END-IF, OUTPUT, JOBLIB, JCLLIB – but we’re only going to cover JOB, EXEC, and DD here because this is just an introduction. The IBM z/OS MVS JCL Reference Manual is the master reference, and you can read it online for free to get more information.
The operation field is (and must be) delineated by one or more blanks both before and after. This field is always required EXCEPT on a continuation card.
Operand Field (aka Parameter Field)
After the operation field is terminated with an ending blank or blanks, you have the OPERAND field, also known as the PARAMETER field.
Most of the information you want to convey is in the operands. There are lots of possible operands, but right now we are interested only in a few basic ones. The first operand on the most basic EXEC statement identifies the name of the program you want executed. It is possible to specify, instead of a program name, the name of a JCL procedure – that is, a set of other JCL statements – that will be brought in from someplace else at that exact point in your “deck”. Ultimately the JCL composed by this method must include at least one EXEC statement specifying a program to be run.
If your EXEC statement calls directly for a program to be executed, it will say PGM=ANYNAME for the first (and perhaps only) operand, but of course instead of ANYNAME you put in the name of the program you want: If you want to run IEBGENER, you say PGM=IEBGENER for example.
The other option instead of PGM= is PROC= and that form is used to specify a JCL procedure that is to be brought in. But since PROC= happens to be the default value, they let you just say the name of the JCL proc and leave off the PROC= part. Hence
//STEP1 EXEC OTHERJCL
Is the same as saying
//STEP1 EXEC PROC=OTHERJCL
Usually JCL procedures are stored in a procedure library on the system and if someone else has set them up then you don’t need to think much about what they contain.
However, you can put a JCL procedure within your job (instream, as it’s called) as long as you put it prior to the EXEC statement that will invoke it. Your instream procedure would start with the PROC type of statement and end with the PEND statement (PEND stands for Procedure End). We’re not going to discuss procs in depth in this introductory article, except to point out one common pitfall: if you leave out the PEND statement on an instream PROC then your job will fail and you will get the “job has no steps” message, because everything following the PROC statement will be considered part of the unterminated procedure. Hence no actual request to EXEC the procedure will be found; hence there is no embedded request to execute an actual program. Hence, Job has no steps.
The PROC or PGM operand is called a positional parameter and it has to come first.
Usually a positional parameter does not have a name or an equal sign. The first parameter on the EXEC statement is an exception, used to specify either a PGM or a PROC.
There are two basic types of operands (throughout all types of JCL), keyword and positional. If it says something equals something else, like an equation, that is a keyword parameter, and the thing on the left of the equals sign is the keyword. Otherwise, if there is no equals sign, then the parameter is positional. There are a few exceptions, and the main exception is that one you just saw, the first parameter on the EXEC statement, which is just an unusual hybrid of the two concepts.
When one keyword parameter has to convey more than one piece of information, then you put a set of parentheses on the right of the equal sign; Within the parentheses you list the set of subparameters you need to convey.
The subparameters can be either keyword or positional, and in some cases there is a mix. When both keyword and positional subparameters are listed, the positional ones always come first. We are going to get to examples of this soon. The parameter TIME has positional subparameters of minutes and seconds.
So let’s talk about the other operands you might want to put on the EXEC statement.
The rest of the parameters on the EXEC are keyword and optional.
TIME is an optional keyword parameter. You can say TIME=5 to request 5 minutes of CPU processing time for the program, which is quite a long time if you aren’t doing some enormous task. TIME has subparameters available, and those are positional, with minutes being the first. If you want to ask for only 30 seconds, then you can say TIME=(0,30) indicating zero minutes and thirty seconds, or you can say TIME=(,30) so that the comma functions as a place-holder for the unspecified minutes, and from that the system can know that the 30 is intended to be the second positional subparameter, seconds.
REGION is another optional keyword parameter. Region size is loosely equivalent to the maximum amount of memory the program is allowed to use at any one time. At least that is what it meant originally. Now there is a convoluted way of interpreting the value; Read the separate post on What TSO Region SIZE Really Means to understand how it is interpreted now. REGION in JCL is nearly the same thing as SIZE in the TSO Logon. You can say REGION=0K or REGION=NOLIMIT to request the maximum possible region size available for your program to use. You can leave the parameter off to accept the default the system assigns.
If you want to specify some other amount, you should look in the JCL reference manual to see how to do that, or else at least read the aforementioned post on TSO Region size. It isn’t a straightforward matter of just specifying how much memory you want, because the system has defaults, limits, and algorithms it uses to determine the region you are actually allowed – it takes what you specify and combines that with its strange rules and then sets your limit at some amount it calculates. Not only that but there are different types of memory, such as 24-bit addressable memory and 31-bit addressable memory, and the system also calculates amounts allowed for each type. Some old and/or oversimplified books and write-ups will tell you that REGION specifies the amount of memory/region your program will get, but that is simply not true, and has not been so during this century. In general 32 Megabytes is usually the smallest amount of 31-bit addressable memory your job will be allowed even if you ask for it to be limited to less. Note that the Region size allowed for your program is an upper limit, not a straight up allocation. Memory is actually assigned to the program in chunks at the time the memory is required, until the assigned limit is reached.
PARM is also an optional keyword parameter on the EXEC statement, used to pass a parameter string to the program that will be executed. Not all programs look at the PARM string. If a program does accept a PARM string as input, the format it accepts is entirely determined by that program, and differs from one program to another. Essentially the PARM string is data you supply to a program.
There are other optional parameters available on the EXEC statement too but we aren’t going to talk about any more of them here today.
Okay, quick review:
- We have an ID or delimiter field at the start of a JCL statement, usually // in columns one and two.
- We have a name field, sometimes optional, starting in column 3 and ending at the first blank space.
- The operation field is after that, it identifies the type of JCL statement (such as JOB, EXEC, DD, PROC, PEND, SET, INCLUDE, IF, etc), and the operation field is terminated by another blank space.
- Next beyond that is the operand field.
~~~ End quick Review ~~~
How do you suppose the operand field is terminated?
You take a wild guess: Could it be by another blank? Yes.
So, then, what is a very common source of hidden, hard-to-find errors in operands?
Yes, Blanks embedded within the operands field.
As soon as the system sees a blank after an operand, it terminates the operands field and ignores the rest of the card EXCEPT for column 72 which is still interpreted as a continuation column. Columns 73-80 are ignored, assumed to be reserved for line numbers.
After the operands field, up through column 71, you have the comments field. You may put comments there, provided you precede the start of the comments with at least one blank to separate the comments from the operands.
If you have a comma between two operands – what you intended to be two operands, at any rate – the operand that follows after the blank is turned into a comment. Notice this is not an error and no error message is issued for it. What you thought was another operand is ignored, and it’s up to you to figure out the result of that. Typically there is no error message, you just get peculiar results, like garbled records or your commented-out DISP being ignored.
Are you required to put a character into column 72 if you want to continue a statement onto more than one line? Not at all. In the very distant past it was required. Then they realized that if a line ended in a comma, they could interpret that as implying the next card would be a continuation of the same statement, and the continuation column was redundant, superfluous, surplus to requirements.
So you can end one line after a comma and put more operands on the next line.
The next line has to start with // in columns one and two, followed by at least one blank, and the continued operands must start by column 16 at the latest.
So, // in cols 1 and 2 followed by one to 13 blanks, then more parameters.
The operands must end before column 72 on each card, that is, the last usable column for operands is 71.
Leave a blank prior to the line number field in 73-80. That is, leave column 72 blank. (Unless you actually intend to continue onto another line, in which case you can put something into 72 or not, as long as it’s not part of your parameters.)
If a parameter has a lot of subparameters that don’t fit on one card, split and continue the subparameters in exactly the same way as you would parameters.
You can go on like that for several continuation lines, as long as you have operands to add. But, as you know, if any of the lines has an extra blank after one of the commas, all operands on that line after the blank are thereby transformed magically into comments and instantly disappear from consideration as JCL operands – and you are not informed that they have been ignored.
If your comments run on into the line number field, that is okay as long as none of the characters fall into column 72. If the line happens to be continued onto another line, then it doesn’t matter about column 72, because the worst it can do is indicate that the line is going to be continued, and that is already true. If, however, your comments fall into column 72 on a line that is not being continued any further, then the following line after that one becomes a JCL error (“expected continuation not received”). Okay, that’s continuations and comments.
But wait! You might ask – How do I continue a quoted string onto multiple lines? The PARM field can be up to 100 characters long, right? Not on an 80-byte line it can’t! So come on, what happens with the quoted string? Hnnh??
Okay. Continuing a quoted string.
By quotes we mean single quotes, also called apostrophes.
On the first line of the quoted string, continue the string right up through column 71. In this case, the character in column 71 is going to be used as part of the string. On the next line, put // in columns 1 and 2 as usual, and then start the rest of the parameter string exactly in column 16. If it is still too long, go right on through column 71, using both columns 71 and 16 as part of the data string. Close off the string as usual on the final continuation line. Since you asked.
What you now know:
~ You now know the joys and the pitfalls of comments and continuations.
~ You know about parameters and subparameters, and about keyword vs positional parameters and subparameters, and required vs optional parameters and subparameters.
~ You know the fields of a JCL statement:
- ID or delimiter field,
- name field,
- operation field,
- Operands field or parameter field,
- Continuation column,
- Line number field
~ Plus, you know what a JOB statement and an EXEC statement are.
Sounds like you know a thing or two about basic JCL concepts now.
There are several other types of JCL statements also, but this is an introductory article so we’re just going to introduce DD statements and then you’re good to go, as far as having a basic understanding of JCL. For advanced material, Remember that IBM z/OS MVS JCL Reference Manual – you can read the newest version online and it always has the definitive answers. Well, to the extent that it has the answers, whatever answers it gives are definitive.
Onward to our last type of JCL statement for today:
DD statements – Data Definition
Computer programs on the IBM don’t usually contain embedded names of actual disk files, as programs do on some platforms.
On the mainframe these are called not disk files but disk datasets, and their names are not called file names but rather dataset names (dsnames, or DSNs).
Typically a mainframe program refers to a file internally by a file name, or ddname, of 8 characters or less. When the program runs, that internal file name is mapped to the name of an actual file (a dataset) through the use of a DD statement.
The Name field of the DD statement in the JCL must be the same as the internal file name the program uses to reference the file. That is what connects the actual dataset to the program file name.
The operands field on the DD statement specifies the dataset name with the DSN keyword parameter, that is, as DSN=the.actual.disk.dataset.name
If you do not specify a DSN on the DD statement, the system makes up a name based on the date, time of day, job name and step name. Typically this dataset is then discarded when the step ends, unless you have made other provisions for it in your JCL. Rarely do you really want a nameless temporary dataset, so you want to specify an actual DSN for your program to use.
DISP (disposition) is the other keyword parameter you usually want to specify.
When you specify an input dataset, you want to put DISP=SHR usually, unless you want to prevent other work from using the dataset while your program is processing it. In that latter case you specify DISP=OLD. OLD and SHR are equivalent except that OLD requests an exclusive lock on the dataset name.
What if you want to create a new dataset as output from the program?
Well, DISP has positional subparameters. The default first subparameter is NEW, but the default second subparameter that NEW implies is DELETE. So that means the system will create the dataset and then when the program finishes the dataset will be deleted. Probably not what you want.
You might think you want KEEP as the second subparameter, but don’t be fooled. What you want is probably CATLG. If you just say KEEP, the system will keep the dataset when the job ends, but you might have trouble finding it again if you don’t save the output from the job, so you can see where the system says it put the dataset. If you specify CATLG, the system will not only keep the dataset it creates for you, it will also create a catalog entry – kind of like a phone book listing – so the dataset can be found just by its name. After that happens, you can get to it again by specifying something like this:
//filename DD DISP=SHR,DSN=whatever.it.was.named
In place of “filename” in the above example DD statement, you put the actual ddname (file name) that the program expects to read, for example the ddname you use might be SYSUT1 if the dataset is going to be used for input to IEBGENER. In place of “whatever.it.was.named” you put the actual dataset name that you assigned to the dataset when you created it.
Syntax of JCL examples
It is common practice in JCL examples to use lower-case characters to denote something where you must supply a value, and upper-case characters to denote anything that should be entered exactly as shown. Many books, manuals, online HELPs, and tutorials use this convention. However, you should be aware that there is also another more awkward convention they sometimes use, which I avoid both because it is awkward and because it is sometimes ambiguous: instead of (or even in addition to) lower-case characters, framing apostrophes (single quotes) are sometimes used to denote something where you are expected to substitute a value in place of the quoted string.
I mention that quoted-string practice just so you know what it means when you run into it. I think it is a holdover from a time when early devices could handle letters in upper-case but did not yet have lower-case. At that time using a quoted string seemed a reasonable convention given the few choices they had. Today we have lower-case characters available nearly everyplace, so I avoid using the older quoted-string style.
Back to DD statement parameters – – –
This is a good example of using positional subparameters, just for review. You can say DISP=(NEW,CATLG) or you can say DISP=(,CATLG) and it means the same thing because the default value for the first subparameter of DISP is NEW. (Obviously “default” used this way means the value the system uses for the parameter or subparameter if you don’t specify a value yourself.)
If you say DISP=SHR or DISP=OLD you don’t have to specify the second subparameter and hence you don’t need the parentheses either.
The defaults for DISP,
and for any part of DISP you don’t specify yourself explicitly:
- The default for the first subparameter is NEW.
- The default for the second subparameter depends on the first subparameter.
- If the first is NEW, then the second defaults to DELETE.
- If the first is OLD or SHR, then the second defaults to KEEP.
- The pattern is this: If the dataset already existed, then by default it continues to exist. If the dataset did not already exist, then by default it returns to nonexistence.
There is a third subparameter, which is the conditional disposition for what happens to the dataset if the program bombs (abends, blows up, crashes). (Note that a non-zero condition code – a bad return code – does not count as bombing/crashing. The third subparameter applies only in case of an actual abend. Abend means Abnormal Termination, that is, crash, bomb, blow up, fall over. System code 0C4, aka S0C4, is an example of an abend code, as are B37, D37, E37, 80A, 913, 806, and User abend code 4095 aka U4095. However, the message “Last Condition code = 0012” does NOT signify an abend.) So: Third subparameter takes effect if (and only if) the program abends.
If you are creating a new dataset and you want it to be kept and cataloged regardless of whether the job step that creates it runs brilliantly or fails abysmally, you specify CATLG as both the second and third subparameter:
The second form shown there means the same as the first because NEW is the default.
So you’re okay with positional subparameters now, in principle, right? Right. Good.
Other oddball ID fields are possible besides // in columns 1 and 2:
//* in columns 1 through 3 means the line is a comment.
If you have the JES3 subsystem then //* is also used to identify control statements for JES3, but we aren’t going to cover that here – just wanted you to know that possibility exists, in case you happen to see a JCL deck with lines that look like weird comments.
/* in the first two columns signifies the return to JCL statements after the end of an instream input file. It is not required in most cases. Mostly you see it as a holdover from times past – people just put it in even though they no longer need to do so. However, the /* is also used to signify the termination of an input file that is itself composed of JCL statements, when the input file is an instream file defined using DD DATA. You can see an example a little further below.
In addition, if you have the JES2 susbsystem, then /* can also be used to identify control statements for JES2, but we aren’t going to go into that in detail either. As with JES3 //* control statements, just be aware that such things exist. Look them up in the IBM manual(s) if you need to understand them.
So, on to that DD DATA example.
When you specify a DD statement to designate an input file, it does not need to use an existing disk or tape dataset. You can have input data right in with the JCL deck – what is called instream data.
Typically you precede such an instream file with a DD statement that includes an asterisk as the first positional parameter in the operand field (The only positional DD operand I’m aware of). You see it like this mostly: //SYSIN DD *
If a /* statement occurs anyplace following the DD * statement, the /* terminates the input file. However the /* is not required. The input file is automatically terminated as soon as the system sees more JCL statements: any line with // in columns 1 and 2.
The system has become even smarter than that in fact: If it is processing JCL and it sees some non-JCL thrown in, the system will assume that you mean for those non-JCL lines to be used as an instream input file, and it will further assume the ddname SYSIN for the instream data. You’ll get a message saying //SYSIN DD * generated statement.
However, if you want to include an instream input file containing JCL statements – unusual, but it happens – then you precede your input data with a DD statement that says the word DATA rather than the asterisk. If the instream JCL data contains an embedded /* then you also need to add the keyword parameter DLM (for delimiter) onto the DD DATA statement to assign some different delimiter than /* — you specify a DLM value for the purpose of preventing the embedded /* from terminating your input file prematurely.
//MYJOB1 JOB (account),NAME,CLASS=A,MSGCLASS=X
//STEP1 EXEC PGM=MYPGM
//SYSIN DD *
//MYJOB2 JOB (account),NAME,CLASS=A,MSGCLASS=X
//STEP1 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT2 DD SYSOUT=(A,INTRDR)
//SYSUT1 DD DATA,DLM=QQ
//MYJOB1 JOB (account),NAME,CLASS=A,MSGCLASS=X
//STEP1 EXEC PGM=MYPGM
//SYSIN DD *
In the second example, everything between the SYSUT1 DD statement and the QQ at the end is read into the program and used for the file that the program calls SYSUT1.
Notice the data in the SYSUT1 file in the second example is exactly the same as the entire set of JCL that constitutes the first example.
We mentioned utility programs earlier. IBM supplies a number of such programs for the purpose of doing commonplace functions. IEBGENER is the name of an IBM-supplied utility program that reads a flat file* from the DD statement designated as SYSUT1, and copies the specified file to whatever location is designated by SYSUT2. This is perhaps the most frequently used IBM Utility program ever, so you may as well know it.
*Definition: A flat file, also called a sequential file, is the simplest kind of file, similar to a text file on a PC; any file organized like a card deck, that is to say, no particular special organization, just one record after another. The records have no keys, nothing fancy is going on, the dataset is not a library containing members. You CAN treat one single member of a library as a flat file in many cases, including this example, by specifying the member name enclosed in parentheses at the end of the dataset name in the JCL, for example: //SYSUT1 DD DISP=SHR,DSN=my.library.name(mymember)
Those ddnames are pronounced thus: SYS is like the first syllable of SYSTEM, and UT is like the first syllable of UTILITY; sometimes a gratuitous additional “TEE” sound is added, so it sounds more like SISS YOU TEE. The digit is simply pronounced as itself. So, SYSTEM UTILITY ddname ONE is SISS YOUT WUHN or SISS YOU TEE WUHN, whichever you find easier to say. Other IBM-supplied programs commonly use this naming convention, often continuing on through SYSUT3 and SYSUT4.
In the case of IEBGENER (pronounce the first three letters I.E.B. and then say GENER as if it is the first part of GENERATION) the ddnames function as follows:
SYSUT1: Input file — the file which is to be copied
SYSUT2: Output file, copied from the input file
SYSIN: Control statements, not usually used, but they can specify minor editing
SYSPRINT: System Print file, that is, information and error messages
Okay, so where does the job shown in Example 2 copy the input file? To SYSUT2 – You can remember this with the silliness that the number TWO is pronounced the same as the word TO, so the data is copied TO sysut-TWO.
So, where is that, exactly? INTRDR? Yeah, that is the “internal reader”, a software implementation of a card reader. This piece of JCL takes the job JCL from SYSUT1 and copies it into the internal reader, thus submitting the job to run, pretty much the same as if you had submitted it from a TSO session or read it into the system from real cards.
Sounds like it could be useful someday, having a job that submits another job, but what if you wanted to copy the input file to a disk dataset instead (which is something you’d probably want to do more often) ?
If you want to copy the data into an existing disk dataset and you want to overwrite any existing data that might already be there, replacing the existing data with the new data, then all the operands you need to specify are the DSN plus DISP=OLD
If instead you want to APPEND onto the end of any existing data that might already be in the dataset, then you say DISP=MOD (which is short for modify) instead of DISP=OLD
What if you want to create a brand new dataset? Do you really have to put in all that DCB and SPACE and all that stuff? No, you do not (usually).
As long as you have some other dataset that is already configured in the same way as you want this one to be – same approximate size, same record length, basically similar – then you can use that dataset as a model by specifying the LIKE parameter (usually). In addition to DSN=the.new.dataset, and DISP=(NEW,CATLG), you specify LIKE=the.name.of.the.model.dataset and that is all you need to put on the DD statement for SYSUT2 with IEBGENER (or pretty much any other place you want to create a new dataset):
//SYSUT2 DD DISP=(NEW,CATLG),DSN=the.new.dataset,
What else is there to say about JCL that would be considered basic?
The character set. Dataset names, ddnames, step names, job names, and most JCL names of things can use the following characters: All the uppercase-only letters, all the ten digits, plus the three symbols # @ and $ except in countries where the $ has been replaced by something else, such as the British pound sterling symbol.
Hence the $ is called the national character or national symbol, and the three special symbols together are sometimes called national symbols or national characters. The very first character of a name generally cannot be a number, but must be one of the letters or acceptable symbols.
Lengths of things: Most things are limited to 8 characters in length, though a few are limited to 4 or 6, and with unprecedented largesse a PARM string has been allowed to be 100 characters in length. A dataset name can be up to 44 characters long (for most datasets) but if longer than 8 characters, as most are, the name must be divided up into 8-byte portions separated by dots (decimal, full stop, point). Each such section must begin with a letter or one of the three allowable symbols. HFS files are an exception and follow their own naming conventions, which resemble UNIX file names. The name of a disk or tape volume is limited to a length of 6, BUT the compensation for that restriction is that the name can also start with a number. Names of tape volumes used to be commonly composed of six digits. Other limitations, caveats etc. are listed in the IBM z/OS MVS JCL Reference Manual, which you can read online for free.
That’s it. JCL, meet the reader. Reader, meet JCL. There, you’ve been introduced to JCL. I think you’re going to get along.
Further reading for JCL topic(s):
IBM z/OS MVS V2R2 JCL Reference Manual (downloadable PDF)
IBM z/OS MVS V2R2 JCL User’s Guide (downloadable PDF)
SC23-6864-01, z/OS DFSMSdfp Utilities (online reading, not a PDF)
IEBGENER in above book (online reading, not a PDF):
DFSMS Using Datasets (online reading, not a PDF)
Index(es) of IBM pdfs for V2R2: