EXEC - conditionally execute a file of system commands.

Syntax:

exec pathname [arg]*
$*$Append filename         $*$Append filename :line
$*$BrEak ON                $*$BrEak OFF
$*$CAtch                   $*$COMMAND name :body
$*$CONTinue                $*$COpy ON
$*$COpy OFF                $*$CRUN crunline
$*$Data filename           $*$Data filename :line
$*$DEBUG type              $*$DEcr var
$*$DeLeteCommand name      $*$DeLeteVar name
$*$ERRor :string           $*$EScape character
$*$ExitLoop                $*$FIle
$*$For                     $*$GOto label
$*$IF                      $*$INcLude filename
$*$INcR var                $*$IniT var :string
$*$LaBel label             $*$MArk :string
$*$PREfix :string          $*$PRINT expressions
$*$QUit                    $*$REm comment
$*$RETurn                  $*$ReWind ON
$*$ReWind OFF              $*$SeaRch var keyword
$*$SeaRch var kywd: statmt $*$SEt var = expression
$*$SHift expression        $*$STREscape char
$*$TAlk                    $*$TraCe ON
$*$TraCe OFF               $*$TRap ON
$*$TRap OFF                $*$TTYPrompt :string
$*$USer expression :string $*$WAIT expression :string
$*$WARN :string            $*$WHILE

EXEC Directives

The following list explains all EXEC directives.

$*$Append filename
begins writing text to the specified file. This output is appended to the current contents of the file. The text to be written starts with the line after the $*$Append directive and ends at an appropriate line of the form
$*$EndData filename
where the "filename" is the same as the one on the $*$Append directive. Each line in this block of text is expanded and the result is written to "filename". An $*$EndData line without a filename ends any preceding $*$Append directive.
$*$Append filename :line
appends the given "line" to the specified file. With this format, you do not use $*$EndData.
$*$BrEak
$*$BrEak ON
tells EXEC that BREAK signals should not stop execution.
$*$BrEak OFF
tells EXEC that BREAK signals should stop execution. This is the default when EXEC begins executing.
$*$CAtch
is a specially-named label that matches anything. For example, in
$*$goto x
$*$rewind on
$*$catch
the $*$GOto will jump to the $*$CAtch rather than "x", since $*$CAtch comes first. $*$CAtch will not stop any $*$GOto that comes later in the file. For example, with
$*$catch
$*$rewind on
$*$goto x
the $*$GOto never jumps to the $*$CAtch, even if the $*$CAtch is the first label that EXEC finds in the command file, because $*$CAtch never works for a $*$GOto that comes later in the file.
$*$COMMAND
may take several forms. In the form
$*$COMMAND name : statement
the given statement is associated with "name". Once you have given this definition, you may use statements of the form
$*$name argument argument ...
in the EXEC input. This executes the statement associated with "name", using the given arguments. You may also use $*$COMMAND in the form
$*$COMMAND name
    statements
$*$EndCommand
This associates the given statements with "name". Again, you can use
$*$name argument argument ...
to execute the block of statements, using the given arguments.

The definitions of user-defined commands are stored in memory. This means that the amount of available memory acts as a limit on the number of commands you can define.

EXEC variables are global to all user-defined commands; you cannot define "local" variables within a command.

You may not give a user-defined command the same name as any of the predefined EXEC directives. Statement blocks may NOT contain $*$COMMAND constructs of their own; in other words, you cannot nest $*$COMMAND definitions. See the section "Input Expansion" below for more details that pertain to $*$COMMAND statements.

$*$CONTinue
may be used inside $*$WHILE or $*$For loops. It tells EXEC to skip any remaining statements in the body of the loop and go directly to the top of the loop again. If this occurs in a $*$WHILE loop, EXEC tests the condition expression of the loop to see if the loop should be repeated. If this occurs in a $*$For loop, EXEC evaluates the condition of the loop to see if the loop should be repeated.
$*$COpy
$*$COpy ON
displays every TSS command on the standard output before executing it.
$*$COpy OFF
turns off the $*$COpy facility, so that commands are executed without being displayed. This is the default when EXEC begins executing.
$*$CRUN crunline
invokes "crunline" as a CRUN command line. For example,
$*$crun file
will have the effect of running the command
crun file
However, $*$CRUN has a number side effects on the EXEC session which are discussed below in "EXEC vs. CRUN".
$*$Data filename
begins writing output text to the specified file. This is exactly like the corresponding form of $*$Append, except that output data overwrites the current contents of "filename" instead of being appended on the end of the existing contents. Use an $*$EndData directive to mark the end of the output text, as in
$*$enddata filename
$*$Data filename :line
is exactly like the similar form of $*$Append, except that output data overwrites the current contents of "filename" instead of being appended to the end of those contents.
$*$DEBUG
can display information about various items found in an EXEC file.
$*$DEBUG Vars
displays the types and values of all currently defined variables.
$*$DEBUG Args
displays the types and values of all current command arguments.
$*$DEBUG Labels
displays information about all known statement labels.
$*$DEBUG Files
displays information about all known EXEC files.
$*$DEBUG Commands
displays information about all user-defined commands.
$*$DEBUG All
displays information on all of the above items.
If you simply use
$*$DEBUG
without specifying what kind of information you want, EXEC displays information on variables and arguments.
You can specify ",+" on the end of any $*$DEBUG command, as in
$*$DEBUG Commands,+
This tells EXEC to display more than its usual amount of information on the selected items.
$*$DEcr var
decrements a numeric variable by one.
$*$DeLeteCommand name
deletes the user-defined command with the given name. (You can define commands using the $*$COMMAND directive). EXEC does not issue any kind of message if the specified command does not exist.
$*$DeLeteVar name
deletes the user-defined variable with the given name. (Such variables can be created with $*$SEt or $*$IniT.)
$*$ERRor :string
writes "string" to the standard error output and terminates execution of the EXEC file.
$*$EScape character
sets the escape character for arguments (command line or user) to the given character. Two consecutive escape characters are converted to a single escape character, except in the case of expansion indirection (see below). The default is "#".

If "character" is not specified, $*$EScape sets the escape character back to '#'.

$*$ExitLoop
may be used inside $*$WHILE or $*$For loops. It tells EXEC to skip any remaining statements in the body of the loop and to begin execution again with the statement following the end of the loop.
$*$FIle
switches back to taking input from the command file after a $*$TAlk directive.
$*$FOR
may take several forms. In the form
$*$FOR var=start,end : statement
the variable "var" is assigned the value of the expression "start". (This must be a numeric expression.) EXEC then executes the statement following the colon. This statement may be a TSS command or EXEC directive. After executing the statement, EXEC increments "var" by 1. If "var" is less than the value of the expression "end", EXEC executes the statement again. EXEC keeps repeating and incrementing "var" by 1 until "var" is greater than "end"; note that EXEC executes the statement when "var" equals "end".

$*$FOR can also take the form

$*$FOR var=start,end,incr : statement
In this case, "incr" is an expression giving the amount to increment "var". EXEC increments "var" by this amount each time through the loop until "var" is greater than "end" if "incr" is positive, and less than "end" if "incr" is negative.

$*$FOR can also take either of the forms

$*$FOR var = expr;expr;expr;... : statement
or
$*$FOR var = expr expr expr ... : statement
EXEC evaluates the first expression and assigns the value to "var". It then executes the given statement. After the statement is finished, EXEC evaluates the next expression in the list and assigns the value to "var"; it then executes the statement again. In this way, EXEC executes the statement for each expression in the expression list.

You can have an expression list that combines forms, as in

$*$FOR a = 1,10;"abc";20,10,-1 : statement
This repeats the given statement with "a" taking values from 1 to 10, then the value "abc", then the values going backwards from 20 to 10.

In this form of $*$FOR, the statement is evaluated when the line is read. This means that a statement like

$*$FOR I=1,10,2 : echo #I
the #I will be immediately evaluated as the statement is read (giving you whatever value the variable I has at the time). This is seldom what you want. Typically therefore, you must "escape" the # character by typing two of them, as in
$*$FOR I=1,10,2 : echo ##I
When this statement is read, it is evaluated as "echo #I" so that the variable is incremented appropriately at each repetition.

$*$FOR can also have any of the forms

$*$FOR var=start,end
    statements
$*$EndFor
 
$*$FOR var=start,end,incr
    statements
$*$EndFor
 
$*$FOR var=expr;expr;...
    statements
$*$EndFor
 
$*$FOR var=expr expr expr ...
    statements
$*$EndFor
These let you repeat several statements instead of just one. Statement blocks may contain $*$FOR constructs of their own (nested FORs). See the section "Input Expansion" below for more details that pertain to $*$FOR statements.
$*$GOto label
goes to the specified label or the first $*$CAtch encountered (but see the description of $*$CAtch for more details).
$*$IF
may take several forms. In the form
$*$IF expression : statement
the expression is evaluated and if the result is true (non-zero), EXEC executes the statement following the colon. This statement may be a TSS command or EXEC directive. The form
$*$IF expression : statement1
$*$ELSE : statement2    
is similar. If the expression is non-zero, EXEC executes "statement1"; otherwise it executes "statement2". The colon ":" after $*$ELSE may be omitted.
$*$IF expression1 : statement1
$*$ELseIF expression2 : statement2
$*$ELSE : statement3
is yet another form. If "expression1" is non-zero, EXEC executes "statement1". Otherwise, if "expression2" is non-zero, EXEC executes "statement2". Otherwise, EXEC executes "statement3". Any number of $*$ELseIF statements may appear between the $*$IF and the $*$ELSE. Again, the ":" after $*$ELSE may be omitted.
$*$IF expression
   statements
$*$ELseIF expression
   statements
$*$ELseIF expression
   statements
      ...
$*$ELSE expression
   statements
$*$ENDif
lets you execute blocks of statements depending on the value of expressions. Statement blocks may contain $*$IF constructs of their own (nested IFs). See the section "Input Expansion" below for more details that pertain to $*$IF statements.
$*$INcLude filename
takes input from the specified file. This is similar to executing the contents of "filename" with an EXEC command, but there is a difference. If you use an EXEC command, the contents of "filename" are executed as a fresh EXEC session, with everything re-initialized. With $*$INcLude, the contents of "filename" are executed in the context of the current EXEC session, and can make use of any variables that are currently defined.
$*$INcR var
increments a numeric variable by one.
$*$IniT var :string
assigns the given string value to the variable "var". The variable is created if it does not exist.
$*$LaBel label
defines a label named "label". The label is taken to be the first string of characters up to the first blank or tab, skipping leading blanks or tabs. The remainder of the line is ignored and may be used as comment.
$*$MArk :string
displays the given "string" on the standard output.
$*$PREfix :string
changes the prefix on EXEC directives to the given string. The default prefix is "$*$" for GCOS8, and the null string "" for other systems.
$*$prefix :
sets the prefix to a null string so that EXEC have no special indicator. If EXEC recognizes a statement as a directive, the statement is interpreted as an EXEC directive; otherwise, the statement is taken as a system command.

The sequence $*$PREfix is always recognized as a PREfix directive, even if the prefix for directives is not "$*$". This makes it possible to write "portable" EXEC files that can be invoked no matter what the prefix currently is. The first statement in such an EXEC file can be

$*$PREfix :string
setting the prefix to whatever is used in the rest of the file.
$*$PRINT exp1 exp2 ...
displays the values of the given expressions. Values are displayed without any intervening white space. For example,
$*$print 1 2 3
displays "123". Subsequent output (e.g. from other commands) immediately follows the "3".
$*$print 1 " " 2 " " 3 "*n"
displays
1 2 3
followed by a new-line; subsequent output begins on the next line. You can use commas to insert blank characters between expressions and semicolons (;) to add new-lines, as in
$*$print 1,2,,3;
This produces
1 2  3
followed by a new-line. Note that there is one space between the "1" and the "2", but two spaces between the "2" and the "3" (corresponding to the two commas in the command).
$*$QUit
terminates an EXEC session.
$*$REm comment
is ignored by EXEC. Using $*$REm lines, you can put comments into your EXEC command files. Note that the comment only extends to the end of the line.
$*$RETurn
has several applications. If EXEC is currently executing a named command (created with $*$COMMAND), $*$RETurn terminates execution of that command.

Otherwise, if EXEC is executing a file that was included with $*$INclude, $*$RETurn terminates execution of the file and returns to the previous input source (either the file that contained the original $*$INclude directive or the terminal).

Otherwise, EXEC must be executing from a single EXEC file or from the terminal. In this case, $*$RETurn terminates the EXEC session.

$*$ReWind
$*$ReWind ON
tells EXEC to go back to the beginning of the command file every time it starts searching for a label (because of a $*$GOto directive).
$*$ReWind OFF
tells EXEC to start searching for a label at the $*$GOto directive that jumps to the label. This is the default when EXEC starts executing.
$*$SeaRch var keyword
searches the command line arguments for a match for "keyword" and then puts the match into the user variable. "keyword" may have any of the following forms:
 keyword
+keyword
-keyword
 keyword=
A "keyword" for $*$SeaRch is specified with uppercase letters signifying necessary characters to match, and lowercase letters signifying letters that may be omitted in abbreviations. For example,
$*$search x KeYword
indicates that the minimal abbreviation for KeYword is "ky".

If the $*$SeaRch command specifies "keyword=", EXEC scans its command line for

string=value
where "string" matches "keyword". The accompanying "value" is assigned to "var". If the $*$SeaRch command specifies one of the other keyword forms, EXEC assigns any matching string to "var". For example, if EXEC finds the string "+keyword", it assigns "+keyword" to "var".

When EXEC finds a matching argument, it removes the argument from the argument list (so that it will not be found again.) EXEC removes the argument in the same way that it does a $*$SHift action.

EXEC creates the variable "var" if it doesn't already exist. If there is no argument that matches the specified "keyword" form, "var" remains undefined. See below for more on undefined values.

$*$SeaRch var keyword :statement
is similar to the previous form of $*$SeaRch. If EXEC finds "keyword" on the command line, it assigns the value to "var" and then executes "statement"; if EXEC does not find "keyword", "statement" is not executed. The "statement" can be either a system command or an EXEC directive.
$*$SEt var = expression
sets a variable to the value of the given expression. The type of the variable is the type of the expression's result. The "=" after the variable name may be omitted. If "expression" is a string expression, the string may contain the '*' escape sequences described in "expl if". For example, you can use *" to stand for a double quote character. Contrast this with $*$IniT, where characters are taken literally and *" would be taken as an asterisk followed by a double quote.
$*$SHift expression
evaluates the given expression to obtain an integer value "n", then deletes argument "n" from the EXEC command line argument list. For example,
$*$shift 4-1
deletes argument 3. All the remaining arguments "shift" to close the gap -- old argument 4 becomes new argument 3, old argument 5 becomes new argument 4, and so on.
$*$STREscape char
lets you change the string escape character. The string escape character is used to indicate escape sequences inside strings. For example, if the string escape character is '*', a new-line is written as '*n'; if the character is '\', a new-line is written as '\n'.

The default string escape is '*' on GCOS8 and '\' on other systems. For maximum portability, an EXEC file should explicitly set its string escape to the character that will be used in the rest of the file.

$*$TAlk
stops reading input from the command file and takes it from the terminal instead. To go back to the command file again, enter $*$FIle.
$*$TraCe
$*$TraCe ON
traces statements by displaying them before execution; this is handy for debugging. $*$TraCe displays all statements, system commands and EXEC directives. $*$CoPy only displays system commands.

If you turn on tracing when $*$TraCe is already on, EXEC goes to an increased level of tracing. EXEC displays messages whenever it opens or closes a file, and on the execution or return of a user-defined command. It also prints out what a particular command line matches (whether this is an EXEC directive, system command, or user-defined command).

$*$TraCe OFF
turns off all tracing. If you have turned on $*$TraCe twice to get the increased level of tracing, a single $*$TraCe OFF turns off both levels of tracing. When EXEC begins execution, tracing is turned off.
$*$TRap
$*$TRap ON
tells EXEC to terminate immediately if a system command fails or if EXEC finds some other error (e.g. a syntax error in an EXEC directive). This is the default when EXEC begins execution.
$*$TRap OFF
tells EXEC to keep on going if an error occurs. If a syntax error occurs in an EXEC directive, the whole directive is discarded.
$*$TTYPrompt : stringexp
displays the value of the string expression "stringexp" on the terminal. EXEC does not start a new line after outputting the string; therefore, this can be used to prompt for input. For example,
$*$ttyprompt : now()+"> "
prompts for input using the current time and ">", as in
hh:mm:yy>
If an error occurs as EXEC evaluates the string expression, EXEC outputs an error message and sets the prompt to the null string "".
$*$USer expression :string
evaluates the given "expression" to obtain an integer "n". This should be between 1 and 99. EXEC then displays the given "string" as a prompt on the terminal. Finally, it reads a command line argument from the standard input and uses this input as argument "n" in the argument list. For example,
$*$user 3 :"Enter your name"
prompts the user with the given string, and treats the user's reply as argument 3 in the argument list. The "string" may be omitted, in which case EXEC just reads the standard input without prompting.
$*$WAIT expression :string
evaluates "expression" to get an integer "n". It then outputs "string", and pauses for "n" seconds. If "n" is negative, EXEC does not pause. EXEC does not write a new-line after outputting "string". This means that you can use $*$WAIT to prompt for input and obtain the input on the same line as the prompt.
$*$WARN :string
writes "string" to the standard error output. EXEC continues processing the command file.
$*$WHILE
may take several forms. In the form
$*$WHILE expression : statement
the expression is evaluated and if the result is true (non-zero), EXEC executes the statement following the colon. This statement may be a TSS command or EXEC directive. After executing the statement, EXEC evaluates the expression again; if the expression is still true (non-zero), EXEC executes the statement again. This keeps repeating until the expression is found to be false (zero). Note that if the expression starts out as false, the statement is not executed at all.

$*$WHILE can also take the form

$*$WHILE expression
    statements
$*$EndWhile
This lets you repeat execution of a sequence of statements in the same way that the first form of $*$WHILE repeats a single statement. Statement blocks may contain $*$WHILE constructs of their own (nested WHILEs). See the section "Input Expansion" below for more details that pertain to $*$WHILE statements.

$*$WHILE constructs do not work if the EXEC input is being taken from the terminal. $*$WHILE is only valid inside an EXEC file or a user-defined command.

Description:

EXEC executes a command file. A command file is a text file which may contain system commands and/or the special EXEC directives listed above. EXEC is similar to the standard TSS CRUN command and accepts many files that could be submitted to CRUN; however, you should take note of the points given in the section "EXEC vs. CRUN" below.

The beginning command line argument is always assumed to be the name of the command file. If there are no command line arguments at all, EXEC uses the standard input. Command argument 0 is initially set to the name of the command file being EXECed.

The usual abbreviation conventions apply to directive names. Any or all characters shown in lowercase may be omitted, but all characters shown in uppercase must be present. Directive names may be entered in upper or lowercase.

A number of directives ($*$Append, $*$Data, $*$USer, etc.) end with ":line" or ":string". In all such directives, the ":" is optional. If the ":" is present, the string starts with the next character. If you omit the ":", the string will start with the first non-blank character. Thus, using ":" lets you put leading blanks into a string. You may not omit the colon in an $*$IF directive, or in the forms of $*$For, $*$WHILE, and $*$COMMAND which take a colon.

EXEC directives may be indented freely. Indentation of control constructs like $*$For and $*$WHILE loops can make your EXEC files easier to read, as in

$*$for a = 1,10
    $*$for b = 1,10
        $*$print a*b;
    $*$endfor
$*$endfor

Command Line Arguments - #nn

EXEC creates an argument list consisting of all arguments given on the command line (except the the name "EXEC"), plus all arguments created with the $*$USer directive. The command file can reference these arguments with the construct "#nn", where "nn" is an integer from 0 to 99.

When EXEC processes a line of input from the command file, it replaces "#nn" with the associated argument value.

If a command line argument is contained in single or double quotes, the quotes will be part of the expansion of the #nn construct.

User Variables

A command file may define variables using the following directives:

$*$IniT var string
$*$SeaRch var keyword
$*$SEt var = expression
Variable names consist of any sequence of letters, digits, or the underscore '_' character. The first character may not be a digit. EXEC does not distinguish between the case of letters, so "var", "VAR", "Var", etc. all refer to the same variable.

There are three types of variables: integer, floating point, and string. Integers are numbers without a fractional part; floating point values are numbers with a fractional part; strings are sequences of ASCII characters. The type of a variable is the type of the value assigned to the variable. If you assign a different type of value to a variable, the type of the variable changes. For example, in

$*$set XYZ 3
    ...
$*$set XYZ 4.5
XYZ starts off as an integer variable, then is changed to a floating point variable.

Expressions

Any input line may contain a construct of the form

#(expression)
where "expression" is an expression recognized by EXEC. This is replaced by the result of the expression. For example,
#(VAR+3)
is replaced by the value of the variable VAR plus 3. If the expression is a single letter, the parentheses may be omitted; for example, the expression #(X) (referring to the value of variable X) may be written #X.

Some EXEC directives expect expressions. For these, you do not need to use the #(expr) syntax. For example, you can write

$*$if var > 3 : statement
The #(expr) syntax is only needed when an expression may not be expected.

EXEC expressions may use any of the arithmetic operations or functions recognized by the UW Tools IF command (see "expl if"). EXEC also supports several functions which can be used in expressions:

ARGs(expression)
evaluates the given expression to obtain an integer "n". The result of ARGs is the value of argument "n" in the argument list. If there is no argument "n", the result of ARG is undefined.
ARGs(exp1,exp2)
evaluates "exp1" to obtain an integer "n1" and "exp2" to get an integer "n2". The result of ARG is a string consisting of all arguments from argument "n1" through argument "n2". ARG uses a single blank character to separate each pair of adjacent arguments. If "n2" is -1, the string consists of all arguments from argument "n1" to the end of the argument list. If there are no such arguments, the result of ARG is undefined.
DEFined(exp)
is true (non-zero) if "exp" is a defined value, and zero otherwise. This is equivalent to the expression "type(exp)!='undefined'".
DEFined(exp1,exp2)
evaluates to "exp1" if "exp1" is a defined value, and "exp2" otherwise. This is almost equivalent to the expression
select(!defined(exp1),exp2,exp1)
However, "exp1" is only evaluated once.
EXISTS(string)
is true (non-zero) if "string" is the name of a variable that currently exists in the EXEC session. Otherwise, the result of the function is false (zero).
INfile()
is the name of the standard input file if input has been redirected with a "<file" construct. If input has not been redirected, the result of INfile is undefined.
NARGs()
gives the number of arguments on the EXEC command line, not counting the "exec" and the file name. For example,
exec myfile arg1 arg2 arg3
has three arguments. If you use $*$SHift or $*$SeaRch to reduce the number of arguments, NARGs returns the reduced number of arguments.
OUTfile()
is the name of the standard output file if output has been redirected with ">file" or ">>file". If output has not been redirected, OUTfile returns a null string.
QUOTE(S)
puts double quotes around the string S. QUOTE also puts in a pair of double quotes for each double quote character inside S. For example, if S is
He said, "Hello!" pleasantly.
the result of QUOTE would be
"He said, ""Hello!"" pleasantly."
QUOTE is convenient if you are passing strings as arguments to commands.
STATUS()
is the status value returned by the last system command to be executed as a command line in this EXEC session. This value has the same form as the "status" described in "expl b lib system". (Note: some expressions may execute system commands themselves, e.g. with the CALLSS() function. STATUS() ignores such calls; it only records status values of commands executed by EXEC as command lines.)

When EXEC encounters a name in an expression, it first tries to interpret the name as a variable. If that doesn't work, it tries to interpret it as a function.

For example, "expl if" defines the function "ToDay". Your command file can also set up a variable "today". When EXEC sees a reference to "today", it will interpret it as the command file variable, not the function. However, if EXEC sees a reference to "td", it may interpret that as the function "ToDay", since "td" is a legitimate abbreviation of "ToDay". If you want to be sure to get the function rather than a variable of the same name, put parentheses after the name, as in "Today()".

Input Expansion

For each line of input, EXEC expands all "#nn" and "#(expression)" constructs by replacing them with the associated values. EXEC then evaluates any expressions on the line. For example, consider

$*$set var "3+2"
$*$if #(var)==5 : echo hello
In the $*$IF, "#(var)" is expanded to "3+2", the string value of "var". The result is
$*$if 3+2==5 : echo hello
so the ECHO command is executed.

After expanding # constructs on a line and evaluating any expressions, EXEC looks at the result. If the line is now an EXEC directive, it is interpreted appropriately. This means that you can construct EXEC directives, as in

$*$set PX = "$*$"
#(PX)if 1 : #(PX)quit
which constructs both an $*$IF and a $*$QUIT statement. If the expanded line is not an EXEC directive, it is executed as a system command using CALLSS.

After a $*$Data directive, EXEC expands each subsequent line by replacing # constructs with their associated values. The resulting line is then written into the file that was specified with the $*$Data directive. No lines are executed, either as system commands or EXEC directives. They are simply expanded and written out. EXEC keeps doing this until end-of-file or an appropriate $*$EndData.

In $*$IF/$*$ELseIF/$*$ELSE/$*$ENDif constructs, EXEC skips statements based on conditional expressions. EXEC doesn't do any expansions on lines that it skips. This may lead to surprises in some complicated situations. For example, consider the following:

$*$if condition
$*$set var = "myfile"
$*$data myfile
     ...
$*$enddata #(var)
     ...
$*$else
     ...
$*$endif
If "condition" is true, EXEC works with the statements following $*$IF. The #(var) on the $*$Data directive is expanded, the appropriate $*$EndData is found to match the $*$Data, and everything works as expected. However, if "condition" is false, EXEC does not do expansions as it skips along looking for the $*$ELSE. It does, however, recognize the $*$Data statement; therefore, EXEC tries to skip to an appropriate $*$EndData and then resume its search for the $*$ELSE. When it reaches the $*$EndData, it does not expand #(var) so it doesn't recognize the $*$EndData as a match for the $*$Data. It therefore keeps looking for another $*$EndData, misses the $*$ELSE and $*$ENDif, and generally gets into trouble.

Similar problems can occur if you omit the ':' in a one-line $*$IF statement. EXEC will assume that the $*$IF is the start of a multi-line $*$IF construct and probably will not do what you want.

A special consideration applies to the $*$FOR, $*$WHILE, and $*$COMMAND directives. If you use one of the forms that take ":command", as in

$*$COMMAND P1A : $*$print #1
"#nn" constructs in the command are replaced with the corresponding arguments as soon as the line is read. In the example above, this means that the command $*$P1A always prints the value that was associated with #1 at the time P1A was defined. On the other hand, if you use one of the directive forms that do not take ":command", as in
$*$COMMAND P1B
    $*$print #1
$*$EndCommand
the "#nn" construct is not replaced until the point of execution. This means that $*$P1B prints the current value of #1 whenever P1B is invoked.

Redirection Expansions

If output redirection is specified on the EXEC command line, you can use the construct "#>" inside the EXEC file to stand for ">>outfile" where "outfile" is the standard output file specified in the redirection. This means that the command file can be written

command1 #>
command2 #>
    ...
so that all the commands redirect their output into the specified "outfile".

If the original command line used ">outfile", "outfile" is cleared when EXEC starts. This means that the output contains the concatenated output of all the commands in the command file that use "#>".

If the original command line used ">>outfile", "outfile" is not cleared. This means that the output contains the original contents of "outfile" plus the concatenated output of all the commands that use "#>". The "#>" construct is replaced by a conventional ">>outfile" on the command line of the command being executed, so it only works with commands that allow this form of output redirection.

Similarly, if input redirection is specified on the EXEC command line, the construct "#<" expands to "<infile", where "infile" is the standard input file specified in the redirection. Again, "#<" is replaced by a conventional "<infile" on the command line of the command being executed, so it only works with commands that allow this form of input redirective. In our experience, the input file is only intended for one of the commands in the command file, so you will probably use "#<" on only one command in the command file.

If you do not redirect output on the EXEC command line, a "#>" in the EXEC file is replaced with the null string. Similarly, if input is not redirected, "#<" is replaced with the null string.

Undefined Values

The statement

$*$set VAR
creates a variable VAR but does not give it a value. In this case, we say that VAR has an "undefined" value, and TYPE(VAR) returns "undefined".

$*$SeaRch directives may also create variables with undefined values. With

$*$search var keyword=
"var" is always created if it doesn't exist already. However, its value is undefined if there is no "keyword=value" on the EXEC command line. To get around this, you can use
$*$init var default
$*$search var keyword=
which initializes "var" to a default value, then overrides this default if the command line specifies "keyword=value". Similarly,
$*$search var2 +keyword
$*$search var2 -keyword
leaves "var2" undefined (if neither keyword is on the command line) or set to "+keyword" or "-keyword".

The result of ARG(N) is undefined if there is no Nth argument in the argument list. Similarly, ARG(M,N) is undefined if there are no arguments with numbers from M through N.

If there is an error in evaluating an expression, the result of the expression is undefined. For example,

$*$set X = sqrt(-2)
gets an error and leaves X undefined.

An undefined value may be used as the argument of the TYPE() of DEFined() functions. The undefined value may be assigned to a variable, and can also be expanded in a line. Other uses of an undefined value generally result in an error. When a line containing an undefined expression is expanded, the undefined value is expanded to the null string.

As an example of how you should be cautious about using values that could be undefined, consider the following:

$*$search X Action=
This either sets X to the value given in "action=value" on the command line, or it leaves X undefined. Suppose you then want to check for a specific value.
$*$if X == "run" :/myprog
runs the program "/myprog" if "action=run" was on the command line. However, if X is undefined because there was no "action=" option, EXEC gives an error because the left operand of "==" is undefined. If you write,
$*$if #X == "run" :/myprog
again it works if X is defined. If X is undefined the line is expanded as
$*$if  == "run" :/myprog
and you get a syntax error because there is nothing to the left of the "==". However, if you write
$*$if "#(x)" == "run" :/myprog
it works in all cases.

Using the Null Prefix

Usually, EXEC can distinguish between system commands and internal or user-defined commands because of the use of $*$ as a prefix. However, users who wish to use the same basic command file on different systems may have good reason to get rid of the prefix requirement by using the statement

$*$prefix :
This sets the prefix to a null string, so that there is no visible difference between system commands and internal or user-defined commands.

While a null prefix makes it easier to port command files to another system, it causes problems if a system command name conflicts with an EXEC command name or a user-defined command. In this case, you can force EXEC to interpret a command name as a system command by putting a "!" in front of it. For example,

!print
is assumed to be a system command named "print". Without the "!", EXEC would assume this was the EXEC command $*$PRINT (with a null prefix).

EXEC vs. CRUN

CRUN is basically a method of feeding input to TSS and capturing the output. If TSS is ready to execute a command, the next input line is interpreted as a command line, and the command is executed. If TSS is already executing a command and is looking for input for the command, the next input line is fed as input into the command.

EXEC on the other hand is a method of executing a sequence of commands. EXEC cannot feed input to the commands being called (except whatever can be put on the command line itself). If any commands in the command file need input, they will wait for it to be entered from the terminal. In addition, EXEC has no facility for capturing output. If you want to save output, you must redirect it using the standard facilities of each command.

The CPOS command only works with CRUN. A command file that uses CPOS does not behave properly with EXEC. Replace the CPOS command with $*$IF and/or $*$GOTO commands for EXEC.

EXEC ignores CRUN's $*$BRK, $*$NULL and $*$DELE directives.

If you execute a command like

crun file
inside EXEC, the effect may be surprising. Remember that the function of CRUN is to supply a line from the given file any time the system would normally read from the terminal. However, EXEC only reads from the terminal in a $*$USer directive; the rest of the time it reads from the command file. EXEC will never try to read from the terminal, so it will never see the input that CRUN is ready to feed; that input goes to commands executed by EXEC or to commands executed after EXEC terminates.

The directive

$*$CRUN crun_command
(looking just like a CRUN command with a $*$ in front of it) starts up CRUN and tells EXEC to start reading input from the terminal. The result is that EXEC starts reading commands from "file". If these commands read terminal input, CRUN continues feeding lines from "file" to these commands. If EXEC encounters the end of CRUN input when it tries to read a line, it resumes with the line that came after the $*$CRUN.

In general then, you can use $*$CRUN to feed input to EXEC, and use a normal CRUN command to feed input to the commands that EXEC calls.

You can also use the directive

$*$CRUN
(without any other argument). EXEC checks to see if there is an outstanding CRUN waiting to feed input to any program that tries to read the terminal. If so, EXEC starts reading the terminal, thereby receiving input from CRUN. If and when EXEC encounters the end of this input, it goes back to reading the command file, with the line after $*$CRUN.

EXEC determines the end of CRUN input by looking for the "COUT" that CRUN always puts at the end of the input file. If the file that CRUN is using contains $*$QUIT, CRUN quits immediately, without giving any indication to EXEC. Thus, EXEC still keeps reading the terminal, looking for COUT. In such situations, the best course of action may be to press BREAK or to enter $*$QUIT to quit EXEC.

Notes:

At sites which have installed the secure command loader, the ACCESS command can mark files as directly executable by EXEC. Specifying

access mf /file use/exec/
marks the file so that the command loader can tell that "/file" is to be executed using EXEC. This means that you can execute the file just by typing
filename args
rather than
exec filename args
For example, you could create a file named "/tlis" that contains
slis +h #(arg(2,-1)) #>
You could then execute the command line
tlis file1 file2 file3 >outfile
This would get submitted to EXEC, which would expand the SLIST command to
slist +h file1 file2 file3 >outfile

Because TSS supports such a restricted number of CALLSS levels, EXEC tries to minimize these levels. If the last line of a command file is a TSS command, EXEC executes the command with GOTOSS rather than CALLSS. This doesn't make much difference if the command file contains multiple commands, but if a command file contains a single command like the "/tlis" example above, the practice is useful. In essence, you get the ability to define one-line "aliases" that do not need extra CALLSS levels.

In earlier releases of the UW Tools package, $*$IF directives were handled by expanding all # constructs on the line and then passing the line to the UW Tools IF command. In this release, $*$IF directives are handled directly by EXEC. Old command files should work correctly as they are. With new command files, you no longer have to use a #(expression) construct for the condition expression in an $*$IF statement. For example, consider

$*$set x "a*"+*"b"
$*$if "#x" == "a*"+*"b" : statement
(Remember that in UW Tools commands, *" is a special escape sequence standing for an escaped double quote, i.e. one that doesn't end the string.) The above $*$IF statement would expand to
$*$if "a"+"b" == "a*"+*"b" : statement
and the condition would not be true. However, you can now write
$*$if x == "a*"+*"b" : statement
and the condition expression is true.

See Also:

expl access mf
for further details of the "use/exec/" option.

Copyright © 2000, Thinkage Ltd.