Serving the MPE Shell on a
Platter
Ronald Dupree Moore
Hewlett Packard Company
20 Perimeter Summit Blvd
Atlanta, GA 30319
(404) 648-5441
(404) 648-5450 FAX
ronald_d_moore@hp.com
What is a command file?
MPE has come a long way. In the beginning we had JCWs and UDC to help us automate common system administration task. As computers in general improved over the last thirty years, so has MPE. In the beginning it was like cooking with a wood burning stove. Today we are not only cooking with gas, we're having our meals served to us on a silver platter. This paper will explore serving MPE commands on a platter, the main focus of this paper is to introduce the reader to commands files, scripts, or shell programs. A rose by any other name still smells as sweet. It does not matter which name you use command file, script, or shell program, we are going to learn some fundamental techniques for writing command files. Topics that will be discussed are:
1.
Variables
2.
Parameters
3.
Input
4.
Expressions
5.
Branching
6.
Loops
We'll start with a definition and purpose of command files. A command file is a mechanism for executing multiple Command Interpreter (CI) commands in a prescribed sequence, similar to a procedure. It provides for the use of variables, expression evaluators, and has a structure that simulate the branching, looping, input, and output structures needed for programmatic control, with an interface mechanism providing programmatic access to the CI from application programs. Command files can be used to simplify tasks. Complicated routines requiring multiple commands can be made transparent to users through command files. Some programming tasks can be coded more simply and efficiently with CI commands than with a standard application language. Routines can be written with CI commands and accessed from application programs through the intrinsic facility. Variables can be used by both CI routines and application programs to pass information between routines.
A command file is a regular MPE flat file. All the user needs is read access to execute the command file. As of 6.0 you can execute a command file by having only execute access. That way the user can not read the lines in the file. This is an excellent way to hide the code from the user.
Command files and UDC can both accomplish the same task. Command files do not have the overhead that is associated with UDCs. With UDCs you have to put all the UDC in the same file, identifying a head, body, and tail. After saving the file, you then must issue the SETCATALOG command to turn on the UDC. The overhead comes at logon time when the system is required to scan all UDC that are set for a given user. This adds to the time that it takes during the logon process.
With command files there is no catalog to set, you create a standard text file and type the name of the text file to execute the CI commands stored in the file. There are some down sides to command files. How do you isolate all of your commands into one group, let's say a group named CMD and want everyone in the account to have access to the command files. First you need to store all the command files in the group and modify the access to the group using the following command while logged on as the account manager:
:ALTGROUP CMD;ACCESS=(R:AC,GU;W,A,L,X,S:GU)
This will allow all users of the account to have read access to the files. The other problem is that you have to qualify the filename by supplying the group name. For example, if a user wants to execute a command file named PWD from a group other than CMD to determine the current GROUP that he is logged into, the user would have to type:
PWD.CMD
The way around this is to set the path using the following command.
:SETVAR HPPATH, "!HPGROUP,PUB,PUB.SYS,ARPA.SYS,CMD"
As you have probably guessed this solution is only good during your current session. What happens when you log off? Yes, you're right. You have to type the above command ever time you logon.
So, how do you get over this new hurdle? We can accomplish this with a logon UDC. Here is a sample logon UDC to solve the above problem.
:Editor
/ADD
1 STARTIT
2 OPTION LOGON
3 SETVAR HPPATH, "!!HPGROUP, PUB,PUB.SYS,ARPA.SYS,CMD"
4 **
5 //
/KEEP ACCTUDC,UNN
/EXIT
:SETCATALOG ACCTUDC;APPEND;ACCOUNT
Now, whenever an account member logs on the above PATH will be set. This path points to the CMD group, eliminating the need of qualifying the filename to include the group name CMD.
There are the two tasks that must be done in order to store all of your command files into a group name CMD. First you must modify the group to give everyone in the account read access to the group CMD. Second, you need to create a logon UDC at the account level that will set the PATH to point to the CMD group. If you make an error in keying in the logon UDC and need to go back into the UDC file to make a correction, you must deactivate the UDC by typing the following:
:SETCATALOG ACCTUDC;DELETE;ACCOUNT
Go into the editor to make your corrections. Don't forget to turn the UDC back on using the APPEND option.
What if you want to setup a group to store command files that can be accessed system wide. Create the CMD group in the SYS account, set the access R:ANY, and set the catalog with :SYSTEM instead of :ACCOUNT.
Now that we know what is a command file and how to store them into a common group for all account members, let's learn how to create some useful command files.
Creating and
using Variables in Command Files
I like to think of a variable as a mailbox or storage location in my computer's memory. When we create a variable, we are telling to computer to reserve us a spot in memory and name that spot with the variable name. The command:
:SETVAR SIZE, 80
will create a spot (a mailbox) in memory called SIZE and place the value 80 into the mailbox named SIZE.
Variables can contain textual, numeric, or Boolean data (true or false). The SETVAR command is used to assign a value to the variable. Variable names can be any combination of letters and numbers plus the underscore character, up to a total of 255 characters. Variables must start with a letter or the underscore character.
There are 94 predefined HP variables in 6.0, these variables are session or job temporary. These predefined HP variables are also referred to as system or global variables. You can gain some valuable information about system variables by typing:
:HELP VARIABLES
Some of the system variables are configurable, you can assign values to them for example HPPATH. But remember that system variables are session dependent, when you log off and back on they will have their original value. The following is an example of setting HPPROMPT to display the group that you're currently logged into:
:SETVAR HPPROMPT, "!!HPGROUP: "
When you execute the :CHGROUP command your prompt will reflect the name of the group that you're in. SETVAR is how you create a variable, but when you use the variable (a system variable or a user defined variable), you must proceed the variable name with an exclamation point "!".
Let's explore using variables. Let's create a variable called "NAME".
:SETVAR NAME, "RONALD"
This will create a mailbox called NAME and place the value RONALD into the mailbox . If you want to display the value in a variable (mailbox), you can use the ECHO command. For example to display the value of the variable NAME, you would type the following:
:ECHO !NAME
The word RONALD will be displayed to your screen. The ECHO command is like the PRINT command in the BASIC programming language. ECHO will display whatever follows the word ECHO to the screen, including the content of a variable when the variable is proceeded with the "!".
You can also include the name of a variable inside the string that you store in a variable. Let's create a variable called STR and place the variable name inside the string.
:SETVAR STR, "!NAME IS MY HERO"
What do you think will get displayed if you ECHO !STR to the screen? You're right.
RONALD IS MY HERO will be echoed to the screen. What do you think will happen if we change the value of NAME and redisplay STR to the screen. Well, let's do it and see.
:SETVAR NAME, "NATALIE"
:ECHO !STR
RONALD IS MY HERO will be displayed. Why? When we set the variable STR
SETVAR STR, "!NAME IS MY HERO" the system replaced !NAME with RONALD and the string that got stored into STR was "RONALD IS MY HERO". The system substituted the value of RONALD for !NAME.
What would you have to do to make the value of the variable NAME change in STR when the value of NAME is changed? The answer is in what is "defeferenceing variables". One bang (!) proceeding the variable name means replace the variable with what is stored in the variable. So in the example:
SETVAR STR, "!NAME IS MY HERO"
The value in NAME is substituted in to the string, so before the string is stored into STR, the value in NAME is substituted into the string. You get, "RONALD IS MY HERO", this string is then stored into the variable STR.
Two bangs (!!) will not substitute the value of the variable, it will place the variable name with one bang in front of the variable in the string. For example:
:SETVAR STR, "!!NAME IS MY HERO"
Will evaluate to "!NAME IS MY HERO" and this is what get stored inside of the variable STR. Now that "!NAME" is stored inside of the variable STR instead of RONALD, every time the variable STR is displayed to the screen the value inside of NAME is displayed. Let's review. Type in the following:
:SETVAR NAME, "RONALD"
:ECHO !NAME (RONALD is displayed)
:SETVAR STR, "!NAME IS MY HERO"
:ECHO !STR (RONALD IS MY HERO is displayed)
:SETVAR NAME, "NATALIE"
:ECHO !STR (RONALD IS MY HERO is displayed)
:SETVAR STR, "!!NAME IS MY HERO"
:ECHO !STR (NATALIE IS MY HERO is displayed)
This is why we use two bangs when we set the system variable HPPROMPT. One bang would have hard coded the group name of the group that you were in when you typed the SETVAR command. Two bangs will always display what is in the variable at the time of execution. The value of HPPROMPT is check every time you hit the return key to find the value of what is stored in the variable HPGROUP. So, remember one bang checks once where two bangs checks every time.
Using
Parameters with Command Files
Think of a command file as a program (shell program) and think of parameters as values that are passed to variables inside the program. The order of PARMs inside the program must match the order of the parameters that are passed to the program. When using parameters, I recommend that you don't have more than three parameters. If you are using more than three parameters, I recommend that you use the INPUT statement and prompt the user for the data. We'll talk mare about the INPUT statement later.
Here is a simple command file that I use to send output to a printer:
PARM FILENAME
FILE L;DEV=205 (205 is the logical device number to a printer)
PRINT !FILENAME > *L
The PARM statement creates the variable (mailbox) called FILENAME. The second line of the command file sets up a file equation for a printer. The third line uses Output redirection to send the output of the print command which normally goes to the screen to the printer 205. You can key in the above three lines and save the file as "LP". You might have to ask you system administrator for the ldev number of you printer.
To execute this command file you would type in the following:
:LP filename
If you're using PARMs in your command files the PARM statement must be the first line of the command file. Multiple PARMs can be list on the same line, but I recommend that you limit the number of PARMs to three.
My intent here is to show you some command files that I share with my students when teaching H3217S (MPE/iX Fundamentals) and H3219S (MPE/iX System Administration). I'll give you the command and explain what it is doing.
Let's look at what a COBOL programmer would have to key in to compile his program.
:COB85XL PROG1,PROG1.OBJ
Assuming that the source code lives in the SRC group and this is where the programmer is compiling the program. We could write a command file to compile the source code and store the object code in the OBJ group.
PARM FILENAME
COB85XL !FILENAME,!FILENAME.OBJ
You can type in the above and save the file as COB. To execute the command file you would type:
:COB PROG1
We can also create a command to link the object and store the executable code in the EXEC group.
PARM FILENAME
LINK !FILENAME.OBJ,!FILENAME.EXEC
You can type in the above and save the file as LK. To execute the command file you would type:
LK PROG1
How about a command file to allow you to change groups. Yes, we can do that. Here is the code:
PARM GROUPNAME=!HPHGROUP
CHGROUP !GROUPNAME
There is something new on line one. What is happening with the "=!HPHGROUP"? First of all what is stored in the variable HPHGROUP. This is where the system stores the name of your home group. To more efficiently create command files you need an understanding of how the real command works. CHGROUP will make your present working group, the name of the group following the command. But what happens if you type CHGROUP without specifying a group name. CHGROUP will place you in your home group. Placing an equal sign after a PARM is creating a default for the PARM. In other words if the user type CD DATA, the system will place the user in the DATA group. Let's assume that SRC is the user's home group. If the user type CD (note you did not specify a group after the command file name), the system will use what is after the equal sign (the default value). In this case the system will place the user in his home group, because HPHGROUP contains the value of the users home group.
Can you create a command file called "L" that will perform a LISTFILE? Sure you can. First how does LISTFILE work.
:LISTFILE file-reference, option
The file-reference could be a single filename or it could be a wild card that would display the names of all the files that match the wild card pattern. But what happens if you don't specify a file-reference. The system will assign the "@" wild card for you. Therefore, the "@" is the default file-reference for LISTFILE. The options range from -3 to 9. But what happens if you don't specify an option. The system will assign the value "0"; therefore "0" is the default for LISTFILE. Knowing the above, now you can write the command file for LISTFILE.
PARM FILENAME=@, OPT=0
LISTFILE !FILENAME,!OPTION
On line one we have assigned two PARMs to pass to the command file "L". The first PARM "FILENAME" has a default of "@". The second PARM "OPT" has a default of "0". You could execute the command file by typing:
:L (list all files in the present working group using the defaults "@" and "0")
:L B@ (list all files that start with a B and use the default "0" for the option parm)
:L ,2 (list all files using the default for file-reference "@" and option 2)
:L xfile,3 (list the information in the file label for xfile)
It is important to arrange the PARMs in the correct order, with the file-reference coming first, followed by the file option.
The syntax for aborting a job is tedious to type. A command file would be of great assistance. Can you write a command file to abort a job? Sure you can. Let's look at the code.
PARM JOBID
ABORTJOB #J!JOBID
How about a command file to alter the priority of a job if the JOBFENCE for the system is set to 10. We can do that.
PARM JOBID, JOBPRI=11
ALTJOB #J!JOBID;INPRI=!JOBPRI
Had enough of parameters. Let's look at the INPUT statement.
Using the
INPUT Statement with Command Files
The INPUT statement is used to solicit a response from the user. Let's say you want to create a command file to create users. You could pass the value of the user's name as a parameter or you could ask the user to enter the value using the INPUT statement. For example:
CLS
ECHO This Command File will prompt the user for a user's name and create a user and home group
ECHO for that user, using the value entered.
ECHO
ECHO
INPUT USERNAME, "Please enter the user name: "
NEWUSER !USERNAME;HOME=!USERNAME
NEWGROUP !USERNAME
ECHO
ECHO
LISTUSER !USERNAME
LISTGROUP !USERNAME
ECHO
ECHO
ECHO End of Program
The syntax is:
INPUT variable, "Input string "
Here is another example:
CLS
ECHO
ECHO
ECHO This Command File will prompt the user for the number of the job to delete
ECHO
ECHO
INPUT JOBNUM, "Please enter the job number: "
ABORTJOB #J!JOBNUMBER
ECHO
ECHO
ECHO End of Program
We could have accomplished the same task by using a parameter. For example you could go into the editor and type the follow lines then save the file as "KILL"
PARM JOBNUM
ABORTJOB #J!JOBNUM
To execute the above command file you would type: :KILL 49
This would abort the job with the id "#J49"
Both the INPUT statement and the use of parameters in have their advantages and disadvantages. If you're only passing one or two values to the command file parameters are fine, but if you're passing more than two values I like the INPUT statement. With the INPUT statement, I don't have to worry about the order of the parameters.
Using Expression Substitution with Command Files
MPE provides an expression evaluator that supports a large selection of arithmetic operations, number conversions, file information, and string manipulation operations. You can type :HELP FUNCTIONS to see a list of the available expression evaluators. These expression evaluators form the basis of comparison structures in establishing branching and looping.
The result of an expression must be dereferenced explicitly. This is accomplished by placing the expression in square brackets and preceding it with a bang (!). For example:
CLS
ECHO
ECHO
ECHO This command file will prompt the user for a filename, record size, and the file size
ECHO (the number of records). It will check to see if the file already exist. If the file does not
ECHO exist it will build the file and automatically increase the file size by 20%.
ECHO
ECHO
INPUT FILENAME, "Please enter the filename: "
ECHO
ECHO RECSIZE, "Please enter the record size: "
ECHO
ECHO FILESIZE, "Please enter the file size: "
IF (FINFO("!FILENAME", "EXISTS")) THEN
BUILD !FILENAME;REC=!RECSIZE;DISC=![FILESIZE * 1.2]
ELSE
ECHO !FILENAME already exist
ENDIF
ECHO
ECHO
ECHO
ECHO End of Program
Note the !RECSIZE is explicitly dereferenced outside of square brackets, while FILESIZE is implicitly dereferenced inside of the square brackets.
Here is another command file. This one will tell you the creator of a file.
CLS
ECHO
ECHO
ECHO This command file will prompt the user for a filename.
ECHO It will then display the name of the Creator.
ECHO
ECHO
INPUT FILENAME, 'Please enter the filename: "
SETVAR CREATOR, ![FINFO(FILENAME,"CREATOR")]
ECHO
ECHO
ECHO !CREATOR is the file creator
ECHO
ECHO
ECHO End of Program
Using Branches with Command Files
The IF statement is how we perform branching within command files. Let's look at the three different forms of the IF statement:
Example 1: Simple IF-THEN-ENDIF statement:
IF expression THEN
Some CI command (when true)
ENDIF
Example 2: IF-THEN-ELSE-ENDIF statement:
IF expression THEN
Some CI command (when true)
ELSE
Some CI command (when false)
ENDIF
Example 3: IF-THEN-ELSEIF-ELSE-ENDIF statement:
IF expression THEN
Some CI command (when true)
ELSEIF expression THEN
Some CI command (when true)
Some CI command (when true)
ELSEIF expression THEN
Some CI command (when true)
ELSE
Some CI command (when false)
ENDIF
Example 1 is used when there is only one choice, example 2 is used when there are two choices, and example 3 is used when there is more than two choices.
Note following the first ELSEIF in example 3 that there are two CI command executed. You can execute one or more command when a condition is true or false. I used only one to keep the syntax simple.
Can we write a command file to check to see if a session name was entered at logon time? Sure we can check to see if a user has entered a session name at logon, but we have to do it as a logon UDC rather than as a command file. I use command files when ever possible, but there are two times when you have to use UDCs. One such time is to execute some commands at logon and the other is to mask or hide a CI command. Here is the logon UDC to check for a session name. Inside the EDITOR you would type the following:
STARTIT
OPTION LOGON
IF HPJOBNAME = "" THEN
ECHO You must enter a session name
INPUT JNAME, "Please enter a session name: "
HELLO !HPJOBNAME, !HPUSER.!HPACCOUNT;HIPRI
ENDIF
**
After entering the above, you could keep the file as, ACCTUDC,UNN and exit the EDITOR. Now you have to set the catalog:
:SETCATALOG ACCTUDC;APPEND;ACCOUNT
If the user logs on without a session name the system will notify the user that he/she did not enter a session name. It will prompt the user to enter a session name and then execute the HELLO command to re-logon the user. This is a simple version of forcing the user to enter a session name. We could do additional checking of the session name. But our purpose here was to illustrate how to use the IF-THEN-ENDIF statement. Also note that in the above example the system will never execute the ENDIF statement in the code.
In the next example we'll check to see if a file exist. We will use the IF-THEN-ELSE-ENDIF. Here is the code:
CLS
ECHO
ECHO
ECHO This command file will check to see if a file exists.
ECHO
INPUT FNAME, "Please enter the file name: "
IF (FINFO("!FNAME", "EXITS")) THEN
ECHO !FNAME EXISTS
ELSE
ECHO !FNAME DOES NOT EXIST
ENDIF
ECHO
ECHO
ECHO End of Program
The next example we'll use the IF-THEN-ELSEIF-ELSE-ENDIF to solicit the user's input from a menu:
CLS
ECHO
ECHO
ECHO
ECHO " Main Menu for File Manipulation"
ECHO
ECHO
ECHO " 1. Create a File
ECHO
ECHO " 2. Delete a File
ECHO
ECHO " 3. Display a Files content
ECHO
ECHO " 4. Exit
ECHO
ECHO
INPUT CHOICE, " Enter a number from 1 to 4 "
IF !CHOICE = 1 THEN
CLS
ECHO
INPUT FNAME, "Enter the name of the file to create: "
LISTFILE !FNAME,2
ELSEIF !CHOICE = 2 THEN
CLS
ECHO
INPUT FNAME, "What is the name of the file that you want to delete: "
PURGE !FNAME
ECHO !FNAME has been deleted
ELSEIF !CHOICE = 3 THEN
CLS
ECHO
INPUT !FNAME, "What is the name of the file you to read: "
PRINT !FNAME
ELSEIF !CHOICE = 4 THEN
RETURN
ELSE
CLS
ECHO
ECHO
ECHO You have entered an invalid response, please enter a number from 1 to 4
ENDIF
The above will execute only one choice and then the program will quit. The next section will show you how to put the above code in a loop, that will return to the main menu after every choice. It will only quit when the choice is equal to "4".
Using Loops with Command Files
MPE provides a looping structure through the WHILE and ENDWHILE statements. With expression evaluator functions a series of statements can be repeated as long as a particular condition is true.
First let's use the above menu code and add a loop structure around it:
SETVAR CHOICE " "
WHILE CHOICE <> "4" DO
CLS
ECHO
ECHO
ECHO
ECHO " Main Menu for File Manipulation"
ECHO
ECHO
ECHO " 1. Create a File
ECHO
ECHO " 2. Delete a File
ECHO
ECHO " 3. Display a Files content
ECHO
ECHO " 4. Exit
ECHO
ECHO
INPUT CHOICE, " Enter a number from 1 to 4 "
IF !CHOICE = 1 THEN
CLS
ECHO
INPUT FNAME, "Enter the name of the file to create: "
LISTFILE !FNAME,2
ELSEIF !CHOICE = 2 THEN
CLS
ECHO
INPUT FNAME, "What is the name of the file that you want to delete: "
PURGE !FNAME
ECHO !FNAME has been deleted
ELSEIF !CHOICE = 3 THEN
CLS
ECHO
INPUT !FNAME, "What is the name of the file you to read: "
PRINT !FNAME
ELSEIF !CHOICE = 4 THEN
RETURN
ELSE
CLS
ECHO
ECHO
ECHO You have entered an invalid response, please enter a number from 1 to 4
ENDIF
ENDWHILE
Next we'll look at a LOOP statement that requires an index. What if you had to create accounts every week for training a fresh group of students. If the students logged on as :HELLO MGR.CLASSnn, where nn could be any number from 1 to 50. Here is the code:
CLS
ECHO
ECHO
ECHO This command file will create accounts for student logon.
ECHO It will prompt the user for the number of accounts to create.
ECHO
ECHO
INPUT MAX, "How many accounts do you want to create: "
SETVAR COUNT, 1
WHILE !COUNT <= !MAX
SETVAR ACCT, "CLASS!COUNT"
ECHO Creating !ACCT
NEWACCT !ACCT,MGR
SETVAR COUNT, !COUNT + 1
ENDWHILE
ECHO
ECHO
ECHO !MAX account were created
ECHO
ECHO
ECHO
ECHO End of program
One thing to remember is that you must increment your counter. This is done just before the ENDWHILE statement.
This is just the tip of the iceberg as far as creating command files, there is a manual "Command Interpreter Access and Variables Programmer's Guide", customer order number 32650-60021 that goes into more details about how to create command files, UDCs, and JOBs. We are going to end this paper with and example of a job that will perform your nightly back at 10:00pm every night except Saturday and Sunday.
!JOB BACKUP,MANAGER.SYS;HIPRI
!TELL MANAGER.SYS;The nightly backup job has started
!IF !HPDAY >= 1 AND !HPDAY <= 5 THEN
FILE T;DEV=TAPE
FILE SYSLIST;DEV=LP
STORE @.@.@;*T;DIRECTORY;SHOW=OFFLINE;PROGRESS=5
STREAM BACKUP.PUB.SYS;AT=20:00
ELSE
STREAM BACKUP.PUB.SYS;DAY=1;AT=20:00
ENDIF
To start the process going you would type: :STREAM BACKUP;AT=10:00.
Programming the MPE shell can be fun and rewarding. MPE has provided programming tools to make your life easy as a shell programmer. Return to you system and have fun writing code to automate routine and complex task.
HAPPY SHELL PROGRAMMING - SERVED TO YOU ON THE MPE SHELL.