# Writing Macros

You must write macros on your computer, not on the E Series router. The macros can contain loops, variables, string and numeric values, and conditional statements. Macros can invoke other macros (as long as they are contained within the same macro file), including themselves, but infinite recursion is not permitted. Macros are case-insensitive.

Macros consist of control expressions and noncontrol expressions. Control expressions are enclosed by control brackets, which are angle-bracket and number sign pairs, like this: <# controlExpression #>. Examples of control expressions include the macro name and macro end statements, and while loops. A control expression can include multiple operation statements if you separate the statements with semicolons (;). For example:

    <# i:=0; while i++ < 3 #>

All macros must have names consisting only of letters, numbers, and the underline character (_). The first character of a macro name cannot be a number. If you include more than one macro within a macro file, each macro must have a unique name. The first line of a macro defines the macro’s name:

    <# macroName #>

Noncontrol expressions are not enclosed by control brackets and simply become part of the generated CLI command text.

You must end all macros with the following control expression:

    <# endtmpl #>

You can add comments to your control expressions to clarify the code by prefacing the comment with forward slashes (//) inside the control brackets:

    <# endtmpl //A comment in the macro end expression #>

Text after the // is ignored when the macro is run and is not displayed by the CLI.

You can also add comments outside the control expressions by prefacing the comment with an exclamation point (!). The CLI displays these comments if you use the test or verbose keywords with the macro command; the CLI never interprets these comments as commands.

    !This is a comment outside any control expression

You can improve the readability of a macro by using tabs to indent expressions. Leading and trailing tabs have no effect on the macro output, because they are removed when the macro is run.

Example

The following is a simple macro that you can use to configure the IP interface on the Fast Ethernet port of the SRP module after you have restored the factory defaults:

    <# ipInit #>
    <# ipAddress := env.getline (“ IP Address of System?” ) #>
    ena
    conf t
    int f0/0
    ip addr <# ipAddress; ‘\n’ #>
    ip route 10.0.0.0 255.0.0.0 192.168.1.1
    host pk 10.10.0.166 ftp
    <# endtmpl #>

## Environment Commands

Macros use environment commands to write data to the macro output, to determine a value, or to call other commands. Table 51 describes the environment commands that are currently supported.

Table 51: Environment Commands

Command | Description
--- | ---
env.delay(int delay) | Causes the macro to delay further execution for the number of seconds specified by delay
env.getLine | Prompts the user with a question mark (?) and waits for a response
env.getLine(string prompt-string) | Prompts the user with the value of prompt-string and waits for a response
env.getLineMasked | Prompts the user with a question mark (?), waits for a response, and echoes the response with an asterisk (*) for each character entered by the user
env.getLineMasked(string prompt-string) | Prompts the user with the value of prompt-string, waits for a response, and echoes the response with an asterisk (*) for each character entered by the user
env.argc | Returns the number of arguments passed to the macro
env.argv(n) | Returns the value of the nth argument, such that 1 <= n <= env.argc The returned value is a string, not a number; if you want to use this value for a subsequent numeric operation, you must first convert it to a number with the env.atoi(string) command
env.argv(0) | Returns the name of the macro
env.atoi(string) | Converts the specified string to a numeric value
env.atoi(env.argv(n)) | Converts input values to integers
env.setResult | Sets parameters within a macro for display through the macroData log at the NOTICE severity level following the completion of the macro
env.getErrorCommand | Returns the command string that triggered a macro error
env.getErrorStatus | Returns the reason for a triggered error
env.startCommandResults | Starts the capture of command output
env.stopCommandResults | Stops the capture of command output
env.getResults | Returns one line of output from the capture buffer
env.regexpMatch(string) | Checks a string against a regular expression
env.getRegexpMatch(string) | Extracts a string from a larger string

## Capturing Output of Commands

Macro language commands can start and stop the capture of JunosE command output and save the results.

The env.startCommandResults command starts JunosE output capture and flushes any existing capture buffer. The capture stops when directed by a stop command or when the buffer maximum of 5,242,880 (5MB) characters is reached. The command output in the buffer is stored as normally seen on the terminal output. A <CR> ends a line of buffer data.

The env.stopCommandResults command stops JunosE output captures. The env.getResults command obtains one line of output from the capture buffer.

The env.startCommandResults, env.stopCommandResults, and env.getResults commands apply to one CLI session. There is no effect on CLI sessions other than the CLI session running the macro.

The env.getResults command gets the next line of the capture buffer. Each call gets the next line of the capture buffer. The command returns the first line the first time it is called after a capture start (env.startCommandResults). It resets the next line it returns by passing a line number argument. An argument of 0 or 1 returns the first line.

For example:

    <# env.startCommandResults #>
    show version
    <# env.stopCommandResults #>
    ! possibly other JunosE commands
    <# while something is true #>
    <# outputLine := env.getResults #>
    . . .
          <# endwhile #>

## Adding Regular Expression Matching to Macros

Use the following syntax to check a string against a regular expression:

    env.regexpMatch(stringToMatch, someRegularExpression)

For example, the string outputLine is checked to determine whether it starts with the value System:

    <# if env.regexpMatch(outputLine, "^System") #>
    . . .
    <# endif #>

In this example, the string interface is checked to determine whether it has the correct syntax:

    <# interface := env.argv(1) #>
    <# if env.regexpMatch(interface, "^[0-9]+/[0-9]+$") #>
    . . .
    <# endif #>

## Extracting a Substring Based on Regular Expression Matching

A string can be extracted from a larger string using the following syntax:

    env.getRegexpMatch(stringToMatch, someRegularExpression, occurrence)

This example gets the third occurrence of a sequence of numbers followed by a space, followed by a sequence of letters within string outputLine:

    <# value := env.getRegexpMatch(outputLine, "[0-9]+ [a-zA-Z]+", 3) #>

This example gets the interface value from an interface string:

    <# interface := env.getRegexpMatch(interface, "[0-9]+/[0-9]+", 1) #>

## Adding Global Variables for Availability to the onError Macro

Global variables can be set in any macro and retrieved without being explicitly passed in another macro. The global variable is set with the following syntax:

    env.setVar(name, value)

The name is a quoted string and the value can be a character string or number.

The global variable is retrieved with the following syntax:

    value := env.getVar(name).

The name is a quoted string and the value is the value stored by an earlier env.setVar.

A macro can contain one onError macro. The onError macro is like any other macro file template. There can only be one macro with the name onError in the macro file.

In this example, the macro sets a global variable before using it in a configuration command:

    <# badInt #>
    <# interface := "9/0" #>
    <# env.setVar("interface",interface) #>
    config t
    interface fast <# interface; '\n' #>
    ip add 7.6.5.4 255.255.255.0
    <#endtmpl#>

    <# onError #>
    <# c := env.getVar("interface") #>
    <# setoutput console #>
    <# "begin output\n" #>
    <# "The interface value: ";c; "\n" #>
    <# endsetoutput #>
    end
    <#endtmpl#>

When the macro runs, the global variable interface is set and the interface command contains an invalid interface value. The CLI reports a syntax error and the macro onError is called. Within the onError macro, the global variable interface is retrieved.

    ERX-40-4a-cc#macro b.mac badInt
    Macro 'badInt' in file 'b.mac' starting execution (Id: 18)
    Enter configuration commands, one per line.  End with ^Z.
    The interface command contains a bad interface value.
    ERX-40-4a-cc(config)#interface fast 9/0
                                        ^
    % interface not found

The CLI reports a syntax error and the macro onError is called. Within the onError macro, the global variable interface is retrieved.

    The interface value: 9/0
    The macro terminates.
    Macro 'badInt' in file 'b.mac' ending execution (Id: 18)

## Unique IDs for Macros

Each macro that is started has an associated macro ID. The ID is displayed when the macro is started and when the macro ends. Log messages for macroSchedular also display the unique ID.

For example, macro c in file bench.mac is started on a Telnet session and the unique ID is 25. The following start and end messages are output to the CLI session:

    Macro 'c' in file 'bench.mac' starting execution (Id: 25)
    . . .
    Macro 'c' in file 'bench.mac' ending execution (Id: 25)

## Accurate Use of Error Status When Accessed Ourside of onError Macro

When the status is requested from a macro other than the onError macro, the error status reported is unavailable and the error string is empty.

For example, the following macro gets the command error and status. The macro is not an onError macro so the status is reported as unavailable.

    <# errorStatusTest #>

    <# setoutput console #>
    <# "error: " $ env.getErrorCommand $ "\n" #>
    <# "status: " $ env.getErrorStatus $ "\n\n" #>
    <# endsetoutput #>

    <#endtmpl#>

When you run the macro, the error command is blank and the error status is Status is not available:

    ERX-40-4a-cc#macro b.mac errorStatusTest
    Macro 'errorStatusTest' in file 'b.mac' starting execution (Id: 17)
    error:
    status: "Macro is not onError. Status is not available"

    Macro 'errorStatusTest' in file 'b.mac' ending execution (Id: 17)

## Variables

A local variable enables you to store a value used by the macro while it executes. The macro can modify the value during execution. Local variables can be integers, real numbers, or strings. The initial value of local variables is zero.

Like macros, local variables must have a name consisting only of letters, numbers, or the underline character (_). The variable name must not begin with a number. You must not use a reserved keyword as a variable name. A line that ends with a variable needs a new line character at the end of the line.

## Literals

A literal is an exact representation of numeric or string values. Every number is a literal. Place single or double quotation marks around a string to identify it as a string literal. You can specify special characters within a literal string by prefacing them with a backslash as follows:

quotation mark | \’
double quotation mark | \”
tab | \t
carriage return | \r
new line | \n
string end | \0
backslash | \\

Examples

    42
    98.6
    ‘string literal’
    “ count”
    “ \t this string starts with a tab and ends with a tab \t”

## Operators

You can use operators to perform specific actions on local variables or literals, resulting in some string or numeric value. Table 52 lists the available macro operators in order of precedence by operation type. Operators within a given row are equal in precedence.

Table 52: Macro Operators

Operation Type | Operators |
--- | --- | ---
Extraction | substr() | rand() | round() | truncate() |  |  |
String | $ |  |  |  |  |  |
Multiplicative | * | / | % |  |  |  |
Arithmetic | + | – | ++ | – – |  |  |
Relational | < | > | <= | >= | = | != |
Logical | || | && | ! |  |  |  |
Assignment | := |  |  |  |  |  |
Miscellaneous | [ ] | , | ( ) | . | ; | <# | #>

Table 53 briefly describes the action performed by each operator.

Table 53: Operator Actions

Operation | Operator | Action
--- | --- | ---
Arithmetic (binary) | + | Adds the right and left sides together
Arithmetic (binary) | – | Subtracts the element to the right of the operator from the element to the left of the operator
Assignment | := | Evaluates the elements to the right of the operator, then assigns that value to the local variable to the left of the operator
Combine | $ | Creates a new string by joining the values of the right and left sides; converts any numeric values to strings before joining
Less than | < | Evaluates as true (returns a 1) if the element to the left of the operator is less than the expression to the right of the operator; otherwise the result is false (0)
Greater than | > | Evaluates as true (returns a 1) if the element to the left of the operator is greater than the expression to the right of the operator; otherwise the result is false (0)
Less than or equal to | <= | Evaluates as true (returns a 1) if the element to the left of the operator is less than or equal to the expression to the right of the operator; otherwise the result is false (0)
Greater than or equal to | >= | Evaluates as true (returns a 1) if the element to the left of the operator is greater than or equal to the expression to the right of the operator; otherwise the result is false (0)
Equal to | = | Evaluates as true (returns a 1) if the element to the left of the operator is equivalent to the expression to the right of the operator; otherwise the result is false (0)
Not equal to (logical NOT) | != | Evaluates as true (returns a 1) if the element to the left of the operator is not equal to the expression to the right of the operator; otherwise the result is false (0)
Logical OR | || | Evaluates as true (returns a 1) if the values of either the left or right sides is nonzero; evaluation halts at the first true (1) expression
Logical AND | && | Evaluates as true (returns a 1) if the values of the left and right sides are both nonzero; evaluation halts at the first false (0) expression
Miscellaneous | [ ] | See Invoking Other Macros for usage.
Miscellaneous | , | See While Constructs for usage.
Miscellaneous | ( ) | Groups operands and operators to achieve results different from simple precedence; effectively has the highest precedence
Miscellaneous | . | Provides access to environment commands; see Table 51. Provides access to macros; see Invoking Other Macros
Miscellaneous | ; | Separates operation statements within a control expression
Miscellaneous | <# #> | Encloses control expressions
Multiplication | * | Multiplies the expression to the left of the operator by the expression to the right
Division | / | Divides the expression to the left of the operator by the expression to the right
Modulo | % | Divides the expression to the left of the operator by the expression to the right and returns the integer remainder. If the expression to the left of the operator is less than the expression to the right, then the result is the expression to the left of the operator.
Postincrement | ++ | Increments the variable after the expression is evaluated
Postdecrement | – – | Decrements the variable after the expression is evaluated
Preincrement | ++ | Increments the variable before the expression is evaluated
Predecrement | – – | Decrements the variable before the expression is evaluated
Negation | ! | Reverses the logical state of its operand. 0 is returned for nonzero operands. 1 is returned for operands that evaluate to zero.
Arithmetic (unary) | + | Provides the absolute value of the value
Arithmetic (unary) | – | Provides the inverse of the value
Substring | substr() | Extracts a portion of a string
Randomize | rand() | Generates a random integer between the provided endpoints, inclusive
Round | round() | Rounds the value to the nearest integer
Truncate | truncate() | Truncates a noninteger value to the value left of the decimal point

### Assignment

Use the assignment operator (:=) to set the value of a local variable. The expression to the right of the operator is evaluated, and then the result is assigned to the local variable to the left of the operator. The expression to the right of the operator can include the local variable if you want to modify its current value.

Example

    <# i := i + 1 #>
    <# count := count - 2 #>

### Increment and Decrement

You can use the increment operator (++) to increase the value of a local variable by one. You specify when the value is incremented by the placement of the operator. Incrementing occurs after the expression is evaluated if you place the operator to the right of the operand. Incrementing occurs before the expression is evaluated if you place the operator to the left of the operand.

Example 1

    <# i := 0; j := 10 #>
    <# j := j - i++ #>

In Example 1, the result is that i equals 1 and j equals 10, because the expression is evaluated (10 – 0 = 10) before i is incremented.

Example 2

    <# i := 0; j := 10 #>
    <# j := j - ++i #>

In Example 2, the result is still that i equals 1, but now j equals 9, because i is incremented to 1 before the expression is evaluated (10 – 1 = 9).

Similarly, you can use the decrement operator (– –) to decrement local variables. Placement of the operator has the same effect as for the increment operator.

When a local variable with a string value is used with the increment or decrement operators, the value is permanently converted to an integer equal to the length in characters of the string value.

### String Operations

The combine operator ($) concatenates two strings into one longer string. Numeric expressions are converted to strings before the operation proceeds. The variable local evaluates to “ want a big” :

Example

    \<# local := “ want a ”  $ “ big”  #>

### Extraction Operations

The extraction operations are substring (substr), randomize (rand), round, and truncate. These operators are equal in precedence, and all take precedence over the string operator.

You can use the substring operator (substr) to extract a shorter string from a longer string. To use the substring operator, you must specify the source string, an offset value, and a count value. You can specify the string directly, or you can specify a local variable that contains the string. The offset value indicates the place of the first character of the substring to be extracted; “ 0” indicates the first character in the source string. The count value indicates the length of the substring. If the source string has fewer characters than the sum of the offset and count values, then the resulting substring has fewer characters than indicated by the count value.

Example

    <# local := “ want a ”  $ “ big”  $ “  string”  #>
    <# substr(local, 5, 12) #>The result is “ a big string”
    <# substr(local, 0, 10) #>The result is “ want a big”
    <# substr(“ ready” , 0, 4) #>The result is “ read”

The random operator produces a random integer value from the specified inclusive range; in the following example, the result is between 1 and 10:

    <# number:= rand(1,10) #>

The round operator rounds off the number to the nearest integer:

    <# decimal:= 4.7 #>
    <# round(decimal) #>The result is decimal is now 5

The truncate operator truncates noninteger numbers to the value left of the decimal point:

    <# decimal:= 4.7 #>
    <# truncate(decimal) #>The result is decimal is now 4

## Arithmetic Operations

The arithmetic operations are multiply (*), divide (/), modulo (%), add (+), and subtract (-). Multiply, divide, and modulo are equal in precedence, but each has a higher precedence relative to add and subtract. Add and subtract are equal in precedence.

Example

    <# 4 % 3 + 12 - 6 #>The result is 7

When a local variable with a string value is used with arithmetic operators, the value is temporarily converted to an integer equal to the length in characters of the string value. You can use the env.atoi commands to avoid this situation.

### Relational Operations

The relational operations compare the value of the expression to the left of the operator with the value of the expression to the right. The result of the comparison is 1 if the comparison is true and 0 if the comparison is false.

If the expressions on both sides of the operator are strings, they are compared alphabetically. If only one expression is a string, the numeric value is used for comparison. Arithmetic operators have a higher precedence.

Example

    <# i := 9; i++ < 10 #>The result is 1
    <# i := 9; ++i < 10 #>The result is 0

### Logical Operations

You can use the logical operators AND (&&), OR (||), and NOT (!) to evaluate expressions. The result of the operation is a 1 if the operation is true and 0 if the operation is false.

For the logical AND, the result of the operation is true (1) if the values of the expressions to the left and right of the operator are both nonzero. The result of the operation is false (0) if either value is zero. The evaluation halts when an expression is evaluated as zero.

For the logical OR, the result of the operation is true (1) if the values of the expression on either the left or right of the operator is nonzero. The result of the operation is false (0) if both values are zero. The evaluation halts when an expression is evaluated as nonzero.

The NOT operator must precede the operand. The operation inverts the value of the operand; that is, a nonzero expression becomes 0, and a zero expression becomes 1. For the logical NOT, the result of the operation is true (1) if it evaluates to zero, or false if it evaluates to nonzero.

Example

    <# i := 6; i >= 3 && i <= 10 #>The result is 1
    <# i := 1; i >= 3 && i <= 10 #>The result is 0
    <# i := 6; i >= 3 || i <= 10 #>The result is 1
    <# i := 1; i >= 3 && i <= 10 #>The result is 0
    <# i := 5; !i #> The result is 0
    <# i := 5; j := 0; !i && !j #>The result is 0
    <# i := 5; j := 0; !i || !j #>The result is 1

Relational operators have a higher precedence than logical AND and OR. The NOT operator is equal in precedence to the increment and decrement operators.

### Miscellaneous Operations

The positive (+) and negative (-) operations must precede the operand. The result of a positive operation is the absolute value of the operand. The result of a negative operation is the negative value of the operand; that is, a +(-5) becomes 5 and a -(-2) becomes 2. These operators have the same precedence as the increment and decrement operators. If there is an operand on both sides of these operators, they are interpreted as the add and subtract operators.

Example

    <# local_abs := +local #>
    <# local_neg := -local #>

All operations are performed in the order implied by the precedence of the operators. However, you can modify this order by using parentheses (( )) to group operands and operators. Operations within parentheses are performed first. The result is that of the operations within the parentheses.

Example

    <# 4 % (3 + 12) - 6 #>The result is -6
    <# 5 && 2 > 1 #>The result is 1
    <# (5 && 2) > 1 #>The result is 0

Results of control expressions are written to the output stream when the expression consists of the following:

- A single local variable
- A single literal element
- An operation whose result is not used by one of the following operations:

  assignment | predecrement | postdecrement | while
  if | preincrement | postincrement |

Example

    <# localvar #>value of localvar is written
    <# " any string" #>“  any string”  written
    <# 4 % 3 + 12 - 6 #>“ 7”  is written
    <# 4 % (3 + 12) - 6 #>“ -6”  is written
    <# i := i + 1 #>nothing is written
    <# count := (count - 2) #>nothing is written

## Conditional Execution

You can use if or while constructs in macros to enable conditional execution of commands.

### If Constructs

If constructs provide a means to execute portions of the macro based on conditions that you specify. An if construct consists of the following components:

- An opening if expression
- A group of any number of additional expressions
- (Optional) Any number of elseif expressions and groups of associated expressions
- (Optional) An else expression and any associated group of expressions
- An endif expression to indicate the end of the if structure

The if expression and any optional elseif expressions must include a lone environment value command, a local variable, a literal, or some operation using one or more operators.

Only one of the groups of expressions within the if construct is executed, according to the following scheme:

1. The if expression is evaluated. If the result is true (nonzero), the associated expression group is executed.
2. If the result is false (zero), then the first elseif expression, if present, is evaluated. If the result is true (nonzero), the associated expression group is executed.
3. If the result of evaluating the first elseif expression is false (zero), the next elseif expression is evaluated, if present. If the result is true (nonzero), the associated expression group is executed.

   If all elseif expressions evaluate to false (zero) or if no elseif expressions are present, then the else expression group—if present—is executed.

4. This evaluation process continues until an expression evaluates to nonzero. If there is no nonzero evaluation, then no expression group is executed.

You can write an empty expression group so that no action is performed if this group is selected for execution. You can nest if structures within other if structures or while structures.

The following sample macro demonstrates various if structures:

    <#            if_examples                     #>
    <# //---------------------------------------- #>

    <# if 1 #>
    ! This is always output because any nonzero value is “ true.”
    <# endif #>

    <# if 0 #>
    ! This is never output because a value of zero is “ false.”
    <# endif #>

    <# // Here’s an example with elseif and else. #>
    <# color := env.getline("What is your favorite color? ") #>
    <# if color = "red" #>
    ! Red is my favorite color, too.
    <# elseif color = "pink" #>
    ! Pink is a lot like red.
    <# elseif color = "black" #>
    ! Black is just a very, very, very dark shade of red.
    <# else #>
    ! Oh.  That’s nice.
    <# endif #>

    <# // Here’s a nested if example. #>
    <# sure := env.getline("Are you sure that " $ color $ " is your favorite color? ") #>
    <# if substr(sure, 0, 1) = ’y’ || substr(sure, 0, 1) = ’Y’ #>
    <# if color != "black" && color != "white";
    shade := env.getline("Do you prefer dark " $ color $
    " or light " $ color $ "? ") #>
    <# if shade = "dark" #>
    ! I like dark colors, too.
    <# elseif shade = "light" #>
    ! I prefer dark colors myself.
    <# else #>
    ! Hmmm, that’s neither dark nor light.
    <# endif #>
    <# else #>
    ! Oh.  That’s nice.
    <# endif #>
    <# else #>
    ! I didn’t think so!
    <# endif #>
    <# endtmpl #>

### While Constructs

While constructs provide a means to repeatedly execute one or more portions of the macro based on a condition that changes during the execution. A while construct consists of the following components:

- An opening while expression
- A group of any number of additional expressions
- An endwhile expression to indicate the end of the while structure

The while expression must include a lone environment value command, a local variable, a literal, or some operation using one or more operators. Each time that this expression evaluates to nonzero, the associated expression group is executed.

You can place an iteration expression after the while expression. This optional expression is evaluated after each execution of the while expression group.

You can include if structures within a while structure. You can also include special control expressions indicated by the break or continue expressions. The break expression breaks out of the while structure by halting execution of the expression group and executing the first expression after the endwhile statement. The continue expression skips over the rest of the expression group, evaluates any iteration expression, then continues with the execution of the while structure. The while structure is limited to 100,000 repetitions by default. You can nest up to 10 while structures.

Example

The following sample macro demonstrates various while structures:

    <#               while_examples               #>
    <# //---------------------------------------- #>
    <# // Remember that variables are automatically initialized to 0. #>
    ! Table of squares of the first 10 integers:
    <# while ++i <= 10 #>
    !<#i;"  ";i*i;"\n"#>
    <# endwhile #>
    <# // Remember that the value of a string used as an integer is the number. #>
    <# // of characters in the string.                                         #>
    <# stars := "*" #>
    <# while stars < 10, stars := stars $ "*"#>
    !<# stars;"\n" #>
    <# endwhile #>
    <# while stars > 0, stars := substr(stars, 0, stars-1)#>
    !<# stars;"\n" #>
    <# endwhile #>
    <# // An example of the continue and break statements. #>
    <# // Also note that many statements can be grouped. #>
    ! All the positive even numbers less than 11
    <# i:=0; while ++i < 100 #>
    <#if i%2; continue; endif; if i > 10; break; endif; "!" $ i $ "\n"; #>
    <# endwhile #>
    <# // While constructs will NOT iterate forever. #>
    <# while 100 > 0 // This is always true, but the macro will eventually stop #>
    <# ++iterations; endwhile #>
    ! The while loop iterated <#iterations#> times.
    <# endtmpl #>

## Passing Parameters in Macros

You can pass parameters to an entry macro. The system translates these parameters to the correct data type.

| Note: The env.argv array is separate from this feature and still functions as designed. In other words, the env.argv array continues to pass parameters as text strings. To use env.argv array values for subsequent numeric operations, you must first convert the values to a number by using the env.atoi(string) command.

Example

The following macro (saved as m.mac) uses values specified in a CLI command to compute the final result:

<# m(left,right,third) #><# multi := left * right #><# multiFinal := multi * third #><# setoutput console #><# “ The result is: multiFinal; “ \n” #><# endsetoutput #><# endtmpl #>

The following example provides the output from using this macro:

host1#macro m.mac m 5 6 7 host1#The result is: 210

## Generating Macro Output

You may want a macro to provide output while it is operating. In simple cases, you can use the verbose keyword to echo commands to the display and display comments as the macro executes. For more information about the verbose keyword, see Example 2 in Invoking Other Macros.

When running more complex macros or macros that contain a lot of commands or comments, you may want to output only certain information (that is, not all commands and comments). In this case, you can use <# setoutput console #> to send the information directly to the console display when it executes.

Example 1

The following example shows how you can send output directly to the console:

    <# setoutput console #>
    This message appears in the console window (whether or not you use verbose mode).
    <#endsetoutput #>

Example 2

The following example shows how you can send a single argument to the console:

    <# puts (msg) #>
    !==================================================================
    !==================================================================
    ! output "msg" to console
    !==================================================================
    !==================================================================
    <# setoutput console #>
    <# msg; "\n"#>
    <#endsetoutput #>
    <# endtmpl #>
    !==================================================================
    !==================================================================
    <# tmpl.puts(“ Hello World” )

## Invoking Other Macros

Macros can invoke other macros within the same macro file; a macro can also invoke a macro from another macro file if the invocation takes place in literal text, that is, not within a control expression. A macro can invoke itself directly or indirectly (an invoked macro can invoke the macro that invoked it); the number of nested invocations is limited to 10 to prevent infinite recursion.

Within each macro, you can specify parameters that must be passed to the macro when it is invoked by another. You must specify named variables enclosed in parentheses after the macro name in the first line of the macro, as shown in this example:

    <# macroName (count, total) #>

Additional parameters can be passed as well. Parameters can be local variables, environmental variables, literals, or operations. The invoking macro passes local variables by reference to the invoked macro. Passing parameters has no effect on the invoking macro unless the parameter is a local variable that is changed by the invoked macro. When the invoked macro completes execution, the local variable assumes the new value for the invoking macro.

The invoked macro can use the param[ n ] expression to access parameters passed to it, where n is the number of the parameter passed. This is useful if optional parameters can be passed to a macro or if the same iterative algorithm needs to process the parameters.

Use the expression param[0] to return the total number of parameters passed to the macro. Use the return keyword to halt execution of the invoked macro and resume execution of the invoking macro. Use the exit keyword to halt execution of all macros.

Example 1

The following sample macro demonstrates macro invocation:

    <#            invoking_examples               #>
    <# //---------------------------------------- #>
    <# name := env.getline("What is your first name? ") #>
    ! First, <#name#>, we will invoke the if_examples and
    ! the while_examples macros...
    <# tmpl.if_examples; tmpl.while_examples #>
    ! Hey <#name#>, have you noticed that your name backwards is:
    !<# eman:= ""; tmpl.reversestring(name, eman); eman; "\n"#>
    <#  tmpl.argumentlist("a", "b", "c")#>
    <# endtmpl #>
    <# argumentlist #>
    <# if param[0] = 0; return; endif #>
    ! argumentList() was called with the following arguments:
    <# while ++i <= param[0]#>
    ! <#param[i];"\n"#>
    <# endwhile #>
    <# endtmpl #>
    <# reversestring (string, gnirts) #>
    <# i := 0 + string; // i is now equal to the number of characters in string. #>
    <# while --i >= 0; gnirts := gnirts $ substr(string, i, 1); endwhile #>
    <# endtmpl #>

Example 2

The following macro in file macro1.mac invokes a macro from within another file, macro2.mac:

    <# callAnotherMacro #>
       <# localVar := 5 #>
       macro macro2.mac macroName2 <# localVar #> string1
    <# endtmpl #>

This macro passes the value of localVar to macroName2. The value of localVar remains at 5 for callAnotherMacro, regardless of any operations upon that variable in the second macro. In other words, an invoked macro in another file cannot return any values to the invoking macro.

The output of callAnotherMacro looks like this:

    host1# macro verbose macro1.mac callAnotherMacro
    host1#!Macro 'callAnotherMacro' in the file 'macro1.mac' starting execution (Id: 55)
    macro macro2.mac macroName2 5 string1
    !Macro 'macroName2' in the file 'macro2.mac' starting execution
    !Macro 'macroName2' in the file 'macro2.mac' ending execution
    host1#!Macro 'callAnotherMacro' in the file 'macro1.mac' ending execution (Id: 55)

The invoked macro cannot invoke a third macro from another file. Only a single level of invocation is supported.