Sunday, December 20, 2020

Writing a REXX program to copy Mainframe tape dataset to DASD

Hiya! ๐Ÿ‘‹ Welcome to my blog. In this post, let's look at a REXX (not T-REX๐Ÿฆ–) code which will take a Mainframe tape dataset name as input and let you copy it to a DASD dataset.  

Mainframe tape datasets  can't be browsed like a PS dataset. To view the contents of the tape dataset, we need to copy the tape dataset to a DASD first. 

I hope this post will be of help to those who don't have prior experience in writing REXX programs. If you read till the bottom of this post, you'll get a fair idea on the following stuff:

  • Usage of REXX in IBM z/OS. 
  • Defining File-tailoring skeletons. 
  • Addressing environments in REXX. 
  • Built-in functions.
  • IF/THEN/ELSE instructions in REXX. 
  • Invoking REXX Exec.

A short intro about REXX:

  • REXX is a programming language that was developed by IBM. 
  • REXX is easy to learn and use. To prove this, let's write the traditional Hello World๐ŸŒ program in REXX.

    • Create a new member in your own PDS

      The first line of the REXX program SHOULD be a comment (delimited by /* and */) and it must contain characters  'REXX' in it. say in the second line is a REXX function that is used to print messages to the console.

      Save the member and press F3 to go back to PDS members list view. In the line command area, type ex to execute the member.

      No caption needed๐Ÿ˜Ž

  • REXX is very readable, REXX instructions are based on English. 
  • REXX has powerful set of built-in functions. 
  • REXX is an interpreted language that does not require compilation. Each line of code is checked and interpreted into "machine understandable code" before being executed. 
  • REXX runs in all MVS address spaces and on many platforms (there's one for Android too ๐Ÿ‘€). 
REXX is a program langauge swiss army knife for z/OS System Programmers and System Administrators.  

I guess it's enough talking about REXX. Let's begin writing ✍ the program itself. The entire program is shown below and we will go through the program section by section. 


Lines 1 thru 16: 

In REXX, comment is a sequence of characters delimited by /* and */. The first 16 lines are comments and they tell the modifications that have been made to this code right from the creation of this REXX Exec. Wait, What is a REXX Exec? (you may be asking yourself ๐Ÿค”). 

Well, a REXX Exec contain REXX language instructions plus commands that are executed by the host environment. 

๐Ÿ’กIBM recommends that all REXX execs start with a comment that includes the characters 'REXX' within the first line (line 1) of the exec. Failure to do so can lead to unexpected or unintended results in your REXX exec. 


Lines 17 thru 19: 

One of the strengths of REXX is that you can use it to invoke the functions of other products. ADDRESS command temporarily or permanently changes the destination of the commands that are followed next. Commands are strings sent to an external environment. 

๐Ÿ’กADDRESS statement is more similar to sudo (Switch User and DO this) command in Linux, which will make you the root user briefly to perform root user actions like installing a package. 

To use DB2 commands in REXX, you must first address that environment. Likewise, in Line #17, address ispexec is coded to execute the command in line #18 in ISPF environment, the full panel application that we all are addicted to๐Ÿ˜€. 

LIBDEF command is used to define the application-level libraries that will be in effect when the application is running. In Line #18, we use the LIBDEF command to define the Skeleton Library ๐Ÿ’€. Gotcha! A Skeleton library is used to store skeleton files. A Skeleton file can be a JCL like the below: 


This is pretty much a simple job that has a SORT step in it to copy an input dataset, provided in SORTIN DD statement, to the output dataset, defined in SORTOUT DD statement. But, something here is so weird, isn't it?๐Ÿค” You see a lot of ampersands, don't you? Let me list out all the variable names preceded by an ampersand. 

  • &JNUM
  • &DSNME
  • &USRID
  • &MON
  • &DY
  • &HH
  • &MM
  • &SS 

There isn't an input dataset name defined in SORTIN. Instead, you've got &DSNME. You'll be issued with JCL errors when you manually submit this JCL. So, please don't do that ⚠. 

This JCL is meant for REXX. When our REXX exec is running, this skeleton file will be scanned record-by-record by the File-tailoring services in REXX (more about File Tailoring services will be covered later in this post). Each record will be scanned to find any dialog variable names, which are names preceded by an ampersand. When a variable name is found, it's current value is substituted from the REXX exec. 

๐Ÿ’กA skeleton file can be assumed as a template that is non-functional on its own. File-tailoring services read skeleton files and write tailored output that can be used to drive other functions. 

Do you get the whole picture now? We'll use File-Tailoring services in Rexx to substitute values in all those variable names, preceded by an ampersand, in the skeleton file. We'll then have a functional JCL which upon submission will copy the input dataset to the output dataset. Simple! ๐Ÿ˜Ž  


Line #19 uses the ARG instruction. ARG retrieves the argument strings provided to a program or internal routine and assigns them to variables. For example, if you pass the string "IBM z/OS" to the statement, arg company product, then

company contains 'IBM'

product contains 'z/OS'

Line #19 is used to pull the input dataset name and assign it to the variable, dsn.


Lines 20 thru 26 in REXX exec: 

We need an input tape dataset name for the REXX exec to copy it to an output dataset. When the REXX exec is invoked without an argment (i.e., an input dataset name), we should tell the user to invoke the REXX exec with an argument and exit the exec. Line 20 thru 26 does that with the help of say instruction in REXX. 

IF/THEN/ELSE instruction in REXX is used to vaildate the dsn variable. When you have more than one statement under an IF condition, enclose them between a DO and END. 


Lines 27 thru 41 in REXX exec: 

The else part starting from line #27 is executed when the user has invoked the REXX exec with an argument. 

STRIP() is a built-in function in REXX and it is being used in line #29 to remove the leading spaces and trailing spaces as well as single quotes (if any) that surrounds the input argument. The stripped value is assigned to variable, a.

RANDOM() is a built-in function in REXX and it is being used in line #30 to generate a random non-negative whole number between the min and max range that are provided as arguments i.e., 001 and 999 respectively. The random number is assigned to a variable JNUM and this variable name, preceded by an ampersand, is present in the first line of the skeleton file. The usage of RANDOM()function in this exec ensures that the job name is unique every time when the output JCL is created by the file-tailoring services. 

USERID() is a built-in function in REXX and it is being used in line #31. This function returns the TSO User ID. Note that the return value of the function is being assigned to variable that is used in the Skeleton file. 

In line #32, the stripped input dataset name available in the variable a is being assigned to the Skeleton file's variable, DSNME

Pause! Let's do a status check to see where we are right now ✅. 

Till now, we only have a valid job name and an input dataset name to be assigned to the JCL in the skeleton file. We need an output dataset name. The output dataset name should be unique every time when the REXX exec is invoked. To do that, we will be adding the Date and Time values at the last 2 qualifiers of the dataset name. At the time of writing this line, I invoked the REXX exec against a dataset and got the output dataset name as 'Z01071.TAPE.COPY.Z01071.DDEC20.T053624'. The last but 1 qualifier has got the date as 20th December and  the last qualifier contains the time in Thhmmss format. Let's continue. 

In line #33, a variable upper is assigned with sequence of characters from a to z in upper case and in line #34, a variable lower is assigned with sequence of characters from a to z in lower case. We'll be using these variables as argumens to the TRANSLATE() built-in function in REXX.  

In line #35, two built-in functions are used. SUBSTR() and DATE(),

DATE() function returns the local date in dd mon yyyy format by default. When 'U' is passed as an argument to the DATE() function, the local date will be returned in MM/DD/YY format.   The return value from the DATE() function is being used as the string for SUBSTR() function. SUBSTR() function is used to extract a portion of the string. Therefore, line #35 is used to extract the MM from the date string (in MM/DD/YY format) and value is assigned to a variable named m

The CALL instruction in line #36 calls an internal routine, find_month


This routine uses the SELECT instruction in REXX to choose one of the 12 months based on the value in variable, m. return statement at the end of the routine, returns back the control to the line next to CALL instruction. 

Line #37, translates the month value from upper case to lower case using the TRANSLATE()built-in function.  

I hope you will be able to decode the lines 38 thru 41. They use the DATE(), TIME() and SUBSTR() functions to assign values to variables that are used in the Skeleton file.

At the end of line #41, we would have assigned values to all the dialog variable names, preceded by an ampersand, in the skeleton file. It's now time to use the File-tailoring services of REXX to write a tailored JCL that can be edited and submitted by the user. 

Lines 42 thru 47:

File-tailoring services:

To use the file-tailoring services, we must first address the ISPEXEC environment. After addressing the environment, the host environment's commands are passed as strings from the lines 43 thru 47. 

  • FTOPEN TEMPPrepares the file-tailoring process and specifies whether the temporary file is to be used for output. 
  • FTINCL TCSKEL - Specifies the skeleton file named TCSKEL from the skeleton library to be used and starts the file tailoring process. The skeleton file is read record-by-record and the dialog variable names in the skeleton file are assigned with values from the variable pool created by the REXX exec.
  • FTCLOSE -  Ends the file-tailoring process. 
  • VGET ZTEMPF - The file tailoring output is directed to a temporary sequential file. The file name of the temporary file is available in the system variable ZTEMPF. Before editing or submitting the job from the temp. dataset, the VGET service should be invoked to initialize the ZTEMPF.  

In line #48, we have issued EDIT dataset command so that the temporary file will be opened in edit mode for the user. The user after checking the JCL can submit the job by issuing SUB command. 

Invoking the REXX exec:

There are several ways of invoking a REXX exec from foreground or background. 

  1. Easiest of the lot is, after saving a REXX exec, you can issue an ex  command on the member to execute the exec in foreground mode. 
  2. When you want to use the member name of the REXX exec as a line command, you must first allocate your private PDS that contain the REXX Exec(s) to the system libraries SYSEXEC/SYSPROC. Refer the first step in this link for more details on how to do that. You might have to copy paste the REXX exec to your PDS and provide you REXX PDS in the DATASET parameter of the ALLOC command. After the allocation, when you type the member name of a REXX exec, you're private PDS will be scanned by the system and the REXX exec will be executed if it's found in the PDS. 
Only option 2 works with the REXX exec that we've prepared. Invoking the exec will be lot easier when you allocate your private PDS to the system libraries. Let's see how our REXX exec fares out.

First, let's assign the private PDS to system library. 

Note that I've provided my PDS in the DATASET parameter of ALLOC command. 

Type ex on the member to execute the REXX exec. 

Allocation successful! Now, we can use the member name TC as a line command.


Type tc on a tape dataset or a DASD dataset that you want to make a copy. 


Tada! Here is the file tailored output. You may type SUB on the command line to submit this job. You may also type some SORT statements before submitting the job.  


Thx for reading. Please share your thoughts in the comments section. I would be glad to answer to your questions if any. Happy Holidays!