PIKT Logo PIKT

Reference: Pikt Script Language

PIKT Tip Jar
PIKT is user-supported software. 
Please show your support.
 
Home
News
Introduction
Samples
Tutorial
Reference
Software
Authors
Licensing



Forum
Marketplace
Links
SiteSearch
FAQ
Contribute
Donate
ContactUs



Google
Web pikt.org


Pikt Script Language

Script Outline

The general outline of a Pikt script is:

	<script name>
	        init
	               status                  active|inactive
	               level                   emergency|urgent|critical|error|
	                                       warning|notice|info|debug
	               task                    "<script description>"
	               input proc|file|logfile "<process|file|logfile|string>"
	                                           ["<bakfile>"]
	               filter                  "<process>"
	               seps                    "<separators>"
	               dat                     <var> <spec>
	                                       ...
	               keys                    <var> [<var> ...]
	        begin
	               <statement>
	                 ...
	        rule
	               <statement>
	                 ...
	        ...
	        end
	               <statement>
	                 ...
	
Of the script components listed above, just about everything is optional.  For alarm scripts (appearing in .alt files and registered in alarms.cfg), what is essential is a script name (in the left-most column), followed optionally by various combinations of (indented) script sections.  Here are the possibilities:
	        name
	        name begin
	        name end
	        name begin end
	        name init begin
	        name init end
	        name init begin end
	        name init rules
	        name init begin rules
	        name init rules end
	        name init begin rules end
	
For standalone scripts (appearing as .pkt files and registered in programs.cfg), the only essentials are programs statements.  See the Standalone Scripts section following for more details.

As an example of just about the most bare-bones Pikt script possible, here is the obligatory "Hello World" script:

	Birth begin output "Hello, world!"
	
Or, if you prefer, and using a more sensible and readable layout:
	Death
	        end
	               output "Goodbye, cruel world!"
	
When run from the command line, either script simply outputs the indicated text to stdout.

Note that both scripts comply with the requirement that the script name appear in the left-most column, with the subsequent script material either following on the same line or in subsequent indented lines.  Pikt is not in general sensitive to script layout.

Script components will be described in greater detail below.  Viewing sample scripts will also give you a better feel for what is and is not acceptable.  (Look, too, at the test validation scripts for some occasionally far-fetched examples.)

Init Section

After the alarm/script identifier, an init section usually follows.  In the init section, you prepare the groundwork for subsequent script actions.

An alarm is either active or inactive; if inactive, it is bypassed.  (Although it might seem that this field is useless, that is not the case.  An administrator of a machine might not have access to the master control machine and might, for whatever reason, need to deactivate one or more alarm scripts on a client machine.  Changing "active" to "inactive"--on the client machine--achieves this.  Also, we anticipate adding other status levels--"pending," say--for alarm scripts in some future PIKT version.)

The alarm is given one of eight severity levels, analogous to syslog's severity levels.  The severity levels are:

  • emergency
  • urgent
  • critical
  • error
  • warning
  • notice
  • info
  • debug
Note that "urgent" is used in place of syslog's "alert".  You may also use the abbreviations "emerg", "crit", "err", and "warn".

In alert messages, alarms are sorted by severity levels, from "emergency" (highest) to "debug" (lowest).  The idea here is to have more important alarm output appear toward the beginning of alert messages, and less important alarm output appear toward the end.

Having said that, severity level designations are rather arbitrary.  Interpret the meaning of "urgent", or what makes one alarm "critical" while another one merely a "warning", as you wish.

If for some reason you prefer that the alarm order you specify in the alerts.cfg listing determines the order of alarm output, you may override the default by adding this directive in PIKT.conf:

	sort_messages           NO      [or: FALSE, OFF, ...;
	                                 defaults to YES, ...]
	
"task" is a short description of the alarm's purpose.  Note that, although the task description may contain PIKT macros, it may not contain (although this, too, might change in some future PIKT version) any function calls or other variable Pikt elements.  Note, too, that you might need to backslash escape certain Pikt special characters (like "@", "#", "$", "%", "=", etc.) within the task description string.

When a script is run from the command line, status, level and task are pointless, therefore optional.  When run by piktd with the intent to output to an e-mail and/or printer report, a script lacking status, level and task generates an error.  In general, it is best always to include the status, level, and task description.

The primary alarm input is either the output of a process, the full contents of a text file, or logfile updates (new info since the previous alarm run).  If a process, it can be any system process (including multiple processes tied together by pipes) yielding text output.  Pikt does not deal with binary input.

If you add a second file path to 'input file' statements, as in

	input file "/etc/passwd" "/etc/passwd.bak"
	
this signals the pikt script interpreter to open a temporary file, in the example case /etc/passwd.pikttmp, and write each input line to that temporary file--unless you have set the special reserved variable $outlin (or $outline or $outputline) for the current input line, in which case $outlin is written to the temporary file instead of $inlin.

If a script were reviewing the passwd file and encountered some problem for a particular line, a missing password for example, you could do this

	if $password eq ""
	        set $outlin = "$username:*:$uid:$gid:$gecos:$homedir:$shell"
	fi
	
to lock out the account automatically.

To exclude a line, use

	        set $outlin = $nil()
	
If you fail to specify a backup file in the 'input file' statement, $outlin is treated like any ordinary string variable.

When the input file is fully processed (at the end of the script, actually), /etc/passwd is backed up to /etc/passwd.bak (in the example), and the temporary file is moved in place of /etc/passwd--but only if one or more 'set $outlin' statements actually changed anything.  The rewritten input file will have the same ownerships and permissions as the old.  If no changes are indicated (or if there is some problem making the backup, for example a badly specified file path), the input file is left completely unchanged.  (Note that without the backup file specification and any 'set $outlin' statements, input files are also left completely unchanged.)

If you specify 'input "<string>"', this is equivalent to 'input proc "echo <string>"'.

Before script processing, input is passed through an optional filter.  In a proc statement, processes can pass output to a filter through pipes, making a separate filter statement unnecessary.  filter statements are really intended for use with files and logfiles.

Input is processed, line by line.  Before processing, an input line is broken apart, with each part assigned to a variable name.

One or more dat statements map input data to variables.  The dat ("data", too, is legal) statement takes one of three forms.  The ordinal form is:

	        dat <var> [x]
	
where <var> is assigned to the data field in the x position.  By default, data fields are separated by one or more spaces and/or tabs.  You override this with the seps statement.  "<separators>" is a string of one or more characters (including possibly the default space and tab in addition to other characters).

In ordinal dat statements, $ signifies the last field, $-1 signifies the next-to-last field, $-2 the field before that, and so on.

You may combine field specifiers of the form 1, 2, ..., $, $-1, $-2 in the same set of data specifications.

Square brackets ("[" and "]")around the ordinal field number say to strip any leading and/or trailing whitespace characters.  The brackets are optional.

The second form of the dat statement specifies columnar data:

	        dat <var> [x,y]
	
This says to assign <var> to data found in columns x through y.  In a series of such statements, column positions may not overlap, but don't have to be contiguous (i.e., there may be gaps).

Here, too, the optional square brackets say to strip leading/trailing whitespace--even if legitimate white space is found in the indicated columns.  (Whitespace internal to the data string is not stripped.)

The third and final form of the dat statement employs a regular expression:

	        dat "<regexp>"
	
Note that <var>'s are not specified.  Rather, the special variable $0 captures the portion of the input line matching the entire regular expression.  $1 captures the first part of the <regexp> enclosed by parentheses (if any), $2 captures the second parenthesized subexpression, and so on.  You may reference these regexp values also by $[0], $[1], ..., especially within for loops (e.g., $[#i]).  (See the Regular Expressions section of this Reference.)

If no dat statement is given, any or all of the following by default refer to the entire input line: $0, $[0], $inlin, $inline, $inputline.  (Pikt provides synonyms for many of the language keywords, in particular, abbreviated versions of the longer keywords).

Even if you specify numbered or columnar variables in dat statements, you can still access all parsed data by means of $0 ($[0]), $1 ($[1]), ...

Concluding the init section, the optional keys line lists variables used as database lookup keys when referring to history values (values stored from previous script runs).  (See the History Logging section of this Reference.)

Begin, End & Rule Sections

Next come action statements, grouped in begin, end, and rule sections.

The heart of a Pikt script is the main processing loop: A line of input (from a proc, file, or logfile) is read in, then acted upon, the next line is read in and acted upon, and so on until the input is exhausted.  Before input processing, you might have a begin section, for example to initialize some variables or to take some other preliminary actions.  After input processing, you might add an end section, for actions taken at the conclusion of input processing.  (You can also achieve additional data processing loops within a Pikt script using a combination of #fopen()/#popen(), #read(), and #fclose()/#pclose().)

In other words:

	        begin                                    [optional]
	               <statement>
	               <statement>
	                ...
	[while there's another input line]               [optional]
	        rule
	               <statement>
	               <statement>
	                ...
	        rule
	               <statement>
	               <statement>
	                ...
	        ...
	[endwhile]
	        end                                      [optional]
	               <statement>
	               <statement>
	                ...
	
Note that begin, rule, and end sections are individually optional, but the script must have at least one.  That is, you might have a script with only a begin section, or only an end section, or only one or more rule sections, or some combination of the three.  Another requirement is that begin must precede rules, and end must follow begin and rules.  Here are the possibilities:
	        begin

	        end

	        begin
	        end

	        rules

	        begin
	        rules

	        rules
	        end

	        begin
	        rules
	        end
	
The input processing loop comprises one or more rule sections.  Strictly speaking, there is never a need to break up the set of input processing statements into separate rule sections, but doing so helps clarify program logic.  (A rule line is also a handy place to put comments.)  A rule section, then, usually groups together program statements pertaining to a single thing, just one attribute of the current input line.

Standalone Scripts

For standalone scripts (appearing as .pkt files and registered in programs.cfg), such scripts must begin with a comment line giving the path to the pikt script interpreter:

	#!/usr/local/pikt/bin/pikt
	
or as it might appear, with a PIKT macro, in programs.cfg:
        #!=pikt
	
Following that, the script must have one or more Pikt statements, perhaps mixed with blank lines or comment lines.

Here is a sample Pikt script to send a SIGHUP signal to inetd as the script would appear in programs.cfg:

	SigHupInetd             // restart inetd
	                        path "=prgdir/SigHupInetd.pkt"
	                        mode 750 uid 0 gid 1
	        #!=pikt

	        # send a SIGHUP signal to inetd

	        init
	                input proc      # but what about args to inetd?
	#if solaris
	                           "=ps -e -o pid,comm | =grep inetd"
	#elif linux | freebsd | openbsd
	                           "=ps -xc | =grep inetd | =grep -v grep"
	#elif hpux | irix | aix
	                           "=ps -e | =awk '{print $1 " " $4}' | =grep inetd"
	#endif
	                dat $inetdpid  1        # 'pid' is a reserved var name
	                dat $inetdproc $        # unused

	        rule
	                =exec "=kill -HUP $inetdpid"
	
After installing this on the PIKT slave systems with
	# piktc -iv +P SigHupInetd +H ...
	
you would have the SigHupInetd.pkt script in the PIKT prgdir:
	#!/usr/local/pikt/bin/pikt

	# send a SIGHUP signal to inetd

	init
	        input proc      # but what about args to inetd?
	                   "/usr/bin/ps -e -o pid,comm | /usr/bin/grep inetd"
	        dat $inetdpid  1        # 'pid' is a reserved var name
	        dat $inetdproc $        # unused

	rule
	        exec "/usr/bin/kill -HUP $inetdpid"
	
(Note that you can edit Pikt .pkt script files directly without registering them in programs.cfg or installing them from the PIKT master.)

You can directly execute this with

	# SigHupInetd.pkt        [assumes that the PIKT prgdir is in your path]
	
(be sure to 'chmod +x' the script file first) or via piktc with
	# piktc -x +C "=prgdir/SigHupInetd.pkt" +H ...
	
Note several differences with Pikt alarm scripts (appearing within .alt files and registered in alarms.cfg):  (a) the script name appears nowhere in the script (rather, it is in the script file name, in this case, SigHupInetd.pkt); (b) on the client, there is no special indentation (the script must still follow the usual indentation rules in the master programs.cfg); (c) you may embed #-style comments within the script (#-style comments are not supported within alarms.cfg alarm scripts); (d) the piktc preprocessor will honor your line and whitespace layout (and not rewrite the layout as it would when installing alarms.cfg alarm scripts as .alt files).

To repeat:  #-style comments are reserved for standalone, directly executable Pikt scripts maintained within programs.cfg (or perhaps edited directly) and installed as .pkt files.  #-style comments are illegal in alarm scripts maintained within alarms.cfg and installed in .alt files.  (For alarm scripts, just use //-style and /* */ comments instead.)

For #-style comments, there might be instances where the parser interprets #foo-style numerical variables or #foo()-style numerical functions as comments, or vice-versa.  In such instances, the fix is simply to add whitespace after the # or perhaps tweak the line layout.  (Admittedly, the parser is not perfected yet.  Try not to get too carried away with weird commenting and script layouts.)  In general, it's just good practice to add whitespace after '#' for all comments.

Because Pikt scripts might reference history values in .hst files in the PIKT hstdir, and because Pikt scripts write to .log files in the PIKT logdir, it is imperative that the script interpreter be able to find the =piktdir.  Unfortunately, at least several versions of Linux and AIX (and other OSes?) strip directory paths from the script interpreter (i.e., "#!/pikt/bin/pikt" becomes simply "#!pikt", and we have been unable to find a way to convey path information to the pikt script interpreter in a reliable or straightforward way.  There are at least two fixes for this problem:  (a) add the PIKT bindir (e.g., /usr/local/pikt/bin) to root's program PATH; (b) specify homdir in PIKT.conf (so long as your PIKT etcdir is one of the standard locations (/etc, /etc/pikt, /usr/local/etc, /usr/local/etc/pikt).

You can use command-line arguments with standalone Pikt scripts and reference their value within the script as $ARGV[].  #ARGV is the number of command-line arguments.

For example, if you do this

	# foo.pkt 1 bar "this is a test"
	
or
	# pikt +S foo.pkt 1 bar "this is a test"
	
$ARGV[0] is the script name, foo.pkt; $ARGV[1] is "1" (a text string, not the number 1); $ARGV[2] is "bar"; $ARGV[3] is "this is a test"; and #ARGC is 3 (a number, not a string).  For scripts without arguments, #ARGC is zero.

Note that you cannot pass arguments to alarm scripts (in *.alt files), and $ARGV[] and #ARGC are undefined in that context.

Objects, Names & Data Types

At a lower level of detail, a Pikt script consists of a sequence of objects:

	        OBJECT          EXAMPLES

	        keyword         rule, if, endif, set, mail, exec
	        string          "foo", "^[^:]+:([^:]+):", ""
	        number          3, -3, 3.1416, 0
	        operator        +, |, ., -e, ==, &&, !, (, )
	        variable        #x, $a, #y[3], $b[5], $4, %x, @x, PASSWD
	        function        #max(#x,#y), $repeat("-",80), #false()
	
In general, every object serves a semantic purpose.  Hence, and for example, parentheses are not required around an if condition, or the arguments to a for statement.  Nor are semicolons or end-of-lines required to signal the end of a program statement.

Strings must begin and end with a double quote.  Double quotes inside double-quoted strings are forbidden.  (This is changed from earlier versions of PIKT.)  If you need to embed a double quote inside a string, use $char(34) and the concatenation operator (.) instead.  In certain circumstances, you might be able to embed double quotes within single-quoted strings and vice versa.  You may also embed variables and functions within strings.

Variable names must begin with a letter, followed by any number of letters digits and underscores ('_').  Variable names are (practically speaking) unlimited in length.  Case is significant.  (Note: Beginning with PIKT 1.14.0, whitespace between an array name and the opening left bracket is illegal.)

Pikt has three basic data types:

	        TYPE            EXAMPLES

	        strings         "foo", $foo, $left("foo",1)
	        numbers         2, #foo, #value("2")
	        filehandles     FOO
	        (prochandles)
	
So, "$" signifies the string type, "#" signifies the number type, and all-caps signifies the filehandle type.  (prochandles are treated like filehandles.)  Arithmetic may not be performed on filehandles (or prochandles).

Pikt supports both "associative" (string-indexed) and numeric (numerically-indexed) arrays.  (Note: Beginning with PIKT 1.14.0, whitespace between the array name and the opening left bracket is illegal.)

Pikt associative arrays are designated by using a string expression as the array index, for example:

	        set $shell[$1] = $7

	        set #shellcnt["/bin/csh"] += 1

	        if #uid[$user] == 0
	
The string expression may be a simple string, a string function, or any other string expression (e.g., a concatenation).

There are four associative array functions, #keys(), #skeys() (also #sortkeys()), #rkeys() (also #reversekeys()), and #members(), and one special associative array keyword, unset (with its alias, delete).

#keys() maps associative array keys in such a way that you can reference each successive key like so:

	        for $u in #keys($shell)
	                ... $shell[$u] ...
	        endfor
	
or, equivalently:
	        foreach #keys($u, $shell)
	                ... $shell[$u] ...
	        endforeach
	
Internally, pikt maps associative array keys to a special key numeric array with a special array index, so that the previous two examples are equivalent to
	        for #i_u=1 #i_u<=#keys($shell) #i_u+=1
	                ... $shell[$u[#i_u]] ...
	        endfor
	
Under most circumstances, you would use either of the first two for loop forms only.

#skeys() (aka #sortkeys()) is like #keys() except that the array keys are sorted before mapping.  #rkeys() (aka #reversekeys()) is like #skeys() except that the array keys are reverse sorted.

#members() is like #keys() except that it applies to an ad hoc list of array keys specified right in the #members() function:

	        for $u in #members("root", "daemon", "bin", "sys")
	                ... $shell[$u] ...
	        endfor
	
or, equivalently:
	        foreach #members($u, "root", "daemon", "bin", "sys")
	                ... $shell[$u] ...
	        endforeach
	
The 'unset' keyword (aka 'delete') removes an element from an associative array, for example:
	        unset $shell["daemon"]
	
or, equivalently:
	        delete $shell["daemon"]
	
If you are tallying instance counts using associative arrays, you must be careful not to assume that uninitialized variables default to 0.  Instead, you should either do something like this
	        if ! #defined(#a[$d])
	                set #a[$d] = 1
	        else
	                set #a[$d] += 1
	        fi
	
or, better, do this
	        =incr(#a[$d])
	
where the =incr() macro is defined (in macros.cfg) as
	        incr(N)         if ! #defined((N))
	                                set (N) = 1
	                        else
	                                set (N) += 1
	                        fi
	
Pikt supports multi-dimensional numerically indexed arrays of from one to three dimensions.  You can write, for example, things like
	        set $foo[#innum(),5] = $5
	
and
	        set #bar[#x,#y,#z] = #fly[123]
	
Note that, unlike many other languages, array indices start at 1, not 0.  In Pikt, generally speaking, all indexing (in whatever context) begins with 1.  (The exception is that the timing parameters in alerts.cfg follow the usual cron conventions.)  Array indices are any valid numerical expression (hence are computable).  Array indices may go as high as 2147483647, which should be more than enough for anybody, or for any purpose.

There is code in place, but not activated, to take numeric arrays to higher than three dimensions, but three dimensions should be more than enough under most circumstances.  In unusual situations, you might kludge together four and more dimensions in an associative array with something like

	        $foo["$text(ndx1) $text(ndx2) $text(ndx3) $text(ndx4) ..."]
	
but you would probably be better off using another language or rethinking your approach to the problem.

Variables come in three different time forms:

	        TIME FORM       EXAMPLES

	        current         $foo, #foo
	        preceding       @foo           [during the preceding input line]
	        previous        %foo           [during the previous script run]
	
"$" and "#" as variable prefixes refer to current values.  "@" signals the value for this variable when the preceding input line was being processed.  "%" signals the value for this variable during the previous script run.  (File handles never take a preceding or previous form.)

So, in Pikt, there is no need to save input data values from one line to the next.  Values from the previous input loop are stored automatically for you.

@inlin (or @inline or @inputline) recalls the entire previous input line.  As a special case of @, @rdlin (or @rdline or @readline) likewise recalls the entire previous read-in line in a while #read() loop.

The same is true with so-called "history variables."  Pikt stores values in a data file for recall the next time the alarm script runs.  If a value is tied to a particular input data variable (specified in a dat statement) and a particular line of input, Pikt does a keyword lookup (specified in a keys statement) to find the appropriate data value.  (See the History Logging section of this Reference.)

String and arithmetic operators may be applied to preceding and previous variables (as well as current string and numeric variables, of course)--i.e., you may use them freely within expressions, functions, etc.--but their value may not be changed (except internally by Pikt itself).

Even when used in their preceding and/or previous forms, variables still retain their underlying string or numerical data types.

Operators & Expressions

Pikt provides the usual operators, and a few not so usual:

	        LOGICAL       DESCRIPTION

	        &&            logical AND
	        ||            logical OR
	        !             logical NOT
	        ( )           logical grouping
	
(Note that for config file #if-#endif preprocessing, you use the & and | set operators, not && or ||.)

The && and || operators use short-circuiting evaluation.

	        ARITHMETIC    DESCRIPTION

	        +             add
	        -             subtract
	        *             multiply
	        /             divide
	        %             modulus
	        **            exponentiate
	        &             bit AND
	        |             bit OR
	        ^             bit EXOR
	        ~             bit COMP
	        <<            bit left shift
	        >>            bit right shift

	        ==            equals
	        >             greater than
	        <             less than
	        >=            greater than or equal to
	        <=            less than or equal to
	        !=            not equal to
	        <>            not equal to
	        !>            not greater than
	        !<            not less than
	        !>=           not greater than or equal to
	        !<=           not less than or equal to
	
Note the absence of the auto-increment and auto-decrement operators.  Instead, you would use += and -= (see below).

The last four operators above are a bit unusual, and note that != and <> are interchangeable.

	        STRING        DESCRIPTION

	        .             concatenate

	        eq            is identical to
	        ne            is not identical to
	        lt            precedes in dictionary order
	        gt            follows in dictionary order
	        le            precedes in dictionary order,
	                      or is identical to
	        ge            follows in dictionary order,
	                      or is identical to
	
Aside from concatenation, there are many more things one can do with strings using the built-in string functions.
	        ASSIGNMENT    DESCRIPTION

	        =             assign to variable, used with all data
	                      types (string, number, filehandle)
	        +=            add and assign
	        -=            subtract and assign
	        *=            multiply and assign
	        /=            divide and assign
	        %=            modulus and assign
	        **=           exponentiate and assign
	        &=            bit AND and assign
	        |=            bit OR and assign
	        ^=            bit EXOR and assign
	        <<=           bit left shift and assign
	        >>=           bit right shift and assign
	        .=            concatenate and assign

	        FILE          DESCRIPTION

	        -e            exists
	        -z            is zero length
	        -f            is a regular file
	        -d            is a directory
	        -c            is a character special file
	        -b            is a block special file
	        -p            is a pipe
	        -l            is a symlink
	        -S            is a socket
	
(proc tests, similar in format to file tests, are planned for a future PIKT version.)

These operators follow the usual associativity and precedence rules.

So, too, with how objects and operators combine to form expressions.  The usual rules apply (no real surprises here).

Functions

Pikt offers a wide variety of built-in functions.  (More will be developed in the future as the need arises.)  An unusual feature of Pikt functions is that they are data-typed: their return value is signified by either the "$" (for string) or "#" (for number) prefix.  (Functions never return a preceding or previous value, so no functions are of the type @foo() or %foo().)  (Note: Beginning with PIKT 1.14.0, whitespace between the function name and the opening left parenthesis is illegal.)

Note that many of the functions have synonymous names, in some cases abbreviations, in other cases names by which they are known in other popular programming languages.

In the function descriptions below, "x" signifies any expression that evaluates to either TRUE (non-zero) or FALSE (zero); "a" any string expression; "n" any numerical expression; and "F" a filehandle.

First, the number functions (functions that return a numerical value):

	        #abs(n)
	        #absval(n)
	        #absolutevalue(n)
	                returns the absolute value of n

	        #age(a1,a2,a3)
	        #fileage(a1,a2,a3)
	                returns the age in days since a1/a2/a3, whether a1 is a
	                full month name or its three-letter equivalent, and
	                whether a3 is a four-digit year (yyyy) or a time (hh:mm)
	                (a2 must be a date)

	        #and(x1,x2,...)
	                returns TRUE if expressions x1, x2, ... are all TRUE
	                (non-zero); FALSE otherwise (if one or more expressions
	                are FALSE or zero)

	        #ascii()
	                [see #code()]

	        #avg(n1,n2,...)
	        #average(n1,n2,...)
	        #mean(n1,n2,...)
	                returns the average (mean) of the values n1, n2, ...

	        #ceiling(n)
	                returns n rounded up to the nearest integer

	        #code(a)
	        #ascii(a)
	        #ord(a)
	                returns the ASCII value of the first char in a

	        #datevalue(n1,n2,n3)
	                returns the time value for the date n3/n2/n1 (dd/mm/yyyy)

	        #day()
	                returns the current calendar day (1-31)
	        #day(n)
	                returns the calendar day (1-31) of the datevalue n

	        #daynumber()
	                returns the current weekday (1-7)
	        #daynumber(a)
	                returns the weekday (1-7) for the string a ("Sunday",
	                "Monday", ...; or their three-letter abbreviations,
	                "Sun", "Mon", ...)

	        #daysbetween(n1,n2)
	                returns the number of days between the datevalues
	                n1 and n2

	        #defined(x)
	        #defined(a)
	        #defined(n)
	                for x, a, or n; returns TRUE (1) if x resolves to a value,
	                or in the case of variables has appeared in a previous dat
	                or set statement, or has a previous or preceding value;
	                FALSE (0) otherwise; #defined(ERR), #defined(NIL),
	                #defined("<<ERR>>"), and #defined("<<NIL>>") are
	                always FALSE (0)

	        #eoi()
	        #endofinput()
	                returns TRUE if there is no input, if at the last input
	                line, or if in the end section; FALSE otherwise

	        #err()
	                returns ERR

	        #error(a)
	        #error(n)
	                returns TRUE (1), if evaluating a or n produces an error;
	                FALSE (0) otherwise

	        #even(n)
	                returns TRUE, if n is even; FALSE, if n is odd

	        #false()
	                returns FALSE (0)

	        #fclose(F)
	                closes file F; returns TRUE if successful, ERR if
	                unsuccessful

	        #fileage()
	                [see #age()]

	        #fileino(a)
	                for path a, returns its inode

	        #filelinks(a)
	                for path a, returns its link count

	        #fileuid(a)
	                for path a, returns the uid of its owner

	        #filegid(a)
	                for path a, returns the gid of its group owner

	        #fileatime(a)
	                for path a, returns its atime (as a time value)

	        #filectime(a)
	                for path a, returns its ctime (as a time value)

	        #filemtime(a)
	                for path a, returns its mtime (as a time value)

	        #filesize(a)
	                for path a, returns its size (in bytes)

	        #find()
	                [see #search()]

	        #fopen(F,a1,a2)
	                opens file a1 with mode a2 (any of "r", "w", or "a");
	                returns TRUE if successful, ERR if unsuccessful

	        #grgid(a)
	        #grgid(n)
	        #groupgid(a)
	        #groupgid(n)
	                for group name a, or gid n, returns its gid
	                (as recorded in the system group file)

	        #hour()
	                returns the current hour (0-23)
	        #hour(n)
	                returns the hour portion (0-23)
	                of the datevalue/timevalue n

	        #if(x,n1,n2)
	                returns n1, if x is TRUE; else n2, if x is FALSE
	        #if(x,n)
	                returns n, if x is TRUE; else the special numerical
	                value NIL, if x is false

	        #index()
	                [see #search()]

	        #inlen()
	        #inputlength()
	                returns the length (number of chars) of the current
	                input line; 0 if outside the input processing loop

	        #innum()
	        #inputnumber()
	                returns the line number of the current input line;
	                0 if still in the begin section; the line number of
	                the last input line if in the end section

	        #int(n)
	        #integer(n)
	                returns n rounded down to the nearest integer

	        #isalnum(a)
	                returns TRUE, if the first char in a is alphanumeric

	        #isalpha(a)
	                returns TRUE, if the first char in a is a letter

	        #iscntrl(a)
	                returns TRUE, if the first char in a is a control char

	        #isdigit(a)
	                returns TRUE, if the first char in a is a digit

	        #isgraph(a)
	                returns TRUE, if the first char in a is any printing
	                char, except space

	        #islower(a)
	                returns TRUE, if the first char in a is a lower-case
	                letter

	        #isprint(a)
	                returns TRUE, if the first char in a is any printing
	                char, including space

	        #ispunct(a)
	                returns TRUE, if the first char in a is any printing
	                char but neither a space nor alphanumeric

	        #isspace(a)
	                returns TRUE, if the first char in a is a whitespace
	                char

	        #isupper(a)
	                returns TRUE, if the first char in a is an upper-case
	                letter

	        #keys(a)
	                returns the number of keys in the associative array
	                a; maps those keys to a string var when used as
	                'for <stringvar> in #keys(a)'; the keys are sequenced
	                in the order encountered during the script run
	        #keys(a1,a2)
	                returns the number of keys in the associative array
	                a2; maps those keys to string var a1 when used as
	                'foreach #keys(a1, a2)'; the keys are sequenced
	                in the order encountered during the script run

	        #length(a)
	                returns the length (number of chars) of string a

	        #level()
	                returns the current alarm's level, 0 through 7
	                (representing EMERG, ALERT, CRIT, ERR, WARNING,
	                NOTICE, INFO, DEBUG)

	        #max(n1,n2,...)
	        #maximum(n1,n2,...)
	                returns the maximum value among n1, n2, ...

	        #mean()
	                [see #average()]

	        #median(n1,n2,...)
	                returns the median value among n1, n2, ...

	        #members(a2,a3,a4,...)
	                like #keys() (which see), except that returns the
	                number of associative array keys in the sequence
	                a2, a3, a4, ...; maps those keys to a string var
	                when used as 'for <stringvar> in #members(a2, a3,
	                a4, ...); the keys are sequenced in the order
	                specified in the #members() function
	        #members(a1,a2,a3,a4,...)
	                like #keys() (which see), except that returns the
	                number of associative array keys in the sequence
	                a2, a3, a4, ...; maps those keys to a1 when used as
	                'foreach #members(a1, a2, a3, a4, ...); the keys are
	                sequenced in the order specified in the #members()
	                function

	        #min(n1,n2,...)
	        #minimum(n1,n2,...)
	                returns the minimum value among n1, n2, ...

	        #minute()
	                returns the current minute (0-59)
	        #minute(n)
	                returns the minute portion (0-59)
	                of the datevalue/timevalue n

	        #mode(n1,n2,...)
	                returns the mode value among n1, n2, ...

	        #month()
	                returns the current month (1-12)
	        #month(n)
	                returns the month (1-12) of the datevalue n

	        #monthnumber()
	                returns the current month (1-12)
	        #monthnumber(a)
	                returns the month (1-12) for the string a ("January",
	                "February", ...; or their three-letter abbreviations,
	                "Jan", "Feb", ...)

	        #nil()
	                returns NIL

	        #not(x)
	                returns TRUE if x is FALSE (0); else FALSE if x is
	                TRUE (non-zero)

	        #now()
	                returns the current time value (number of seconds since
	                the Unix Epoch, January 1, 1970)

	        #odd(n)
	                returns TRUE, if n is odd; FALSE, if n is even

	        #or(x1,x2,...)
	                returns TRUE if any of expressions x1, x2, ... are TRUE
	                (non-zero); FALSE otherwise (if none of x1, x2, ... are
	                TRUE or non-zero)

	        #ord()
	                [see #code()]

	        #parse()
	                [see #split()]

	        #passwduid()
	                [see #pwuid()]

	        #passwdgid()
	                [see #pwgid()]

	        #pclose(F)
	                closes process F; returns TRUE if successful, ERR if
	                unsuccessful

	        #pid(a)
	        #procid(a)
	        #processid(a)
	                returns the process id (pid) of process a

	        #popen(F,a1,a2)
	                opens process a1 with mode a2 (any of "r", "w", or "a");
	                returns TRUE if successful, ERR if unsuccessful

	        #ppid(a)
	        #pprocid(a)
	        #pprocessid(a)
	                returns the parent process id (ppid) of process a

	        #pwuid(a)
	        #pwuid(n)
	        #passwduid(a)
	        #passwduid(n)
	                for user name a, or uid n, returns its uid
	                (as recorded in the system passwd file)

	        #pwgid(a)
	        #pwgid(n)
	        #passwdgid(a)
	        #passwdgid(n)
	                for user name a, or uid n, returns its gid
	                (as recorded in the system passwd file)

	        #random()
	                returns a random fraction 0 <= r < 1
	        #random(n)
	                returns a random integer from 1 to n

	        #read(F)
	                reads in the next line of input from the file handle F
	                associated with some open file or process; the input
	                line is assigned to the special variable $rdlin (also
	                $rdline or $readline); returns the number of chars
	                read, else ERR on error

	        #rkeys(a)
	        #rkeys(a1,a2)
	        #reversekeys(a)
	        #reversekeys(a1,a2)
	                like #keys() (which see), except that the array keys
	                are sequenced in alphabetically reverse sorted order

	        #rindex()
	                [see #rsearch()]

	        #rfind()
	                [see #rsearch()]

	        #round(n)
	                returns n rounded up or down to the nearest integer
	                (where #round(2.5) --> 3, #round(-2.5) --> -3)

	        #rsearch(a1,a2)
	        #rindex(a1,a2)
	        #rfind(a1,a2)
	                return the index position (beginning at 1) of a2
	                within string a1, with the search starting at the
	                end of the string, else 0 if not found

	        #rule()
	        #rulenumber()
	                returns 0 when called from within a Pikt script begin
	                section, 1 when called within the first rule, 2 when
	                called within the second rule, and so on; if there
	                are x rules within a Pikt script and #rule() is called
	                from within an end section, x+1 is returned

	        #search(a1,a2,n)
	        #index(a1,a2,n)
	        #find(a1,a2,n)
	                returns the index position (beginning at 1) of a2
	                within string a1, with the search starting at
	                position n; else 0 if not found
	        #search(a1,a2)
	        #index(a1,a2)
	        #find(a1,a2)
	                returns the index position (beginning at 1) of a2
	                within string a1, with the search starting at the
	                beginning of the string; else 0 if not found

	        #second()
	                returns the current second (0-59)
	        #second(n)
	                returns the second portion (0-59)
	                of the datevalue/timevalue n

	        #skeys(a)
	        #skeys(a1,a2)
	        #sortkeys(a)
	        #sortkeys(a1,a2)
	                like #keys() (which see), except that the array keys
	                are sequenced in alphabetically sorted order

	        #split(a)
	        #parse(a)
	                decomposes string a into parts assigned to the variables
	                $1, $2, ..., where whitespace (space or tab) separates
	                the constituent parts; returns the number of parts
	        #split(a1,a2)
	        #parse(a1,a2)
	                decomposes string a1 into parts assigned to the variables
	                $1, $2, ...; if a2 is not a regular expression, any of the
	                chars in a2 serve as field separators for the decomposition;
	                else if a2 is a regular expression containing one or more
	                () pairs, the matching constituent parts are assigned to
	                $1, $2, ..., and the entire expression match is assigned
	                to $0; returns the number of parts, or matched parts in
	                the regular expression case
	        #split(a1,a2,a3)
	        #parse(a1,a2,a3)
	                decomposes string a2 into parts assigned to the variables
	                a1[1], a1[2], a1[3], ...; a3 is the field separator(s) for
	                the decomposition; returns the number of parts

	        #system(a)
	                executes the command string a, returning its exit value

	        #timevalue(n1,n2,n3)
	                returns the time value for the time n1:n2:n3 (hh:mm:ss)

	        #today()
	                returns the time value of the current day's beginning
	                (midnight)

	        #true()
	                returns TRUE (1)

	        #trunc(n)
	        #truncate(n)
	                returns n as an integer with its fractional part (if
	                any) lopped off

	        #val(a)
	        #value(a)
	                returns the numerical value of a; or ERR, if a doesn't
	                resolve to a number

	        #weekday()
	                returns the current weekday (1-7)
	        #weekday(n)
	                returns the weekday (1-7) of the datevalue n

	        #write(F,a)
	                writes string a to the file handle F associated with
	                some open file or process; returns the number of
	                chars written; else ERR on error

	        #year()
	                returns the current year (as a four-digit number)
	        #year(n)
	                returns the year (as a four-digit number) of the
	                datevalue n

	        #yearday()
	                returns the current yearday (1-366)
	        #yearday(n)
	                returns the yearday (1-366) of the datevalue n

	        #yearweek()
	                returns the current yearweek (1-53)
	        #yearweek(n)
	                returns the yearweek (1-53) of the datevalue n
	
Next, on to the string functions (functions that return a string value).  Where inappropriate in any of the following functions (indeed, in any of the number functions above), a non-integer numerical argument will have the function return the string "<<ERR>>" (and this error will be logged).
	        $alarm()
	        $script()
	                returns the name of the currently executing alarm script

	        $alert()
	                returns the current alarm's alert name (e.g., "Urgent")

	        $ampm()
	                returns "AM" if the current time falls between
	                midnight and twelve noon, else "PM" if between noon
	                and midnight
	        $ampm(n)
	                returns "AM" if datevalue n's time falls between
	                midnight and twelve noon, else "PM" if between noon
	                and midnight

	        $basename(a)
	                for the path a, strips off the directory portion and
	                returns the terminating filename

	        $char(n)
	                returns the char associated with the ASCII value n

	        $checksum(-3,a)
	                returns the output of the system cksum command for
	                file a
	        $checksum(-2,a)
	                returns the output of the system sum command for
	                file a
	        $checksum(-1,a)
	                returns the output of the system 'sum -r' command for
	                file a
	        $checksum( 0,a)
	                returns the null checksum (0), also the number of
	                bytes, for file a
	        $checksum( 1,a)
	                returns the BSD-style sum computed internally,
	                also the number of 512-byte blocks, for file a
	        $checksum( 2,a)
	                returns the SysV-style sum computed internally,
	                also the number of 512-byte blocks, for file a
	        $checksum( 3,a)
	                returns the POSIX cksum computed internally,
	                also the number of bytes, for file a
	        $checksum( 4,a)
	                returns the MD4 checksum, also the number of bytes,
	                for file a
	        $checksum( 5,a)
	                returns the MD5 checksum, also the number of bytes,
	                for file a

	                note: the checksums assigned to the different levels
	                might change, and more levels might be added in the
	                future to compute, for example, these additional
	                checksums: SNEFRU, HAVAL, SHA

	        $chop(a,n)
	                returns string a with n chars chopped off the end
	        $chop(a)
	                returns string a minus its last char

	        $command(a)
	                executes the command string a, returning its first
	                line of output only; if more than one output line is
	                needed, uses #popen() and #read() instead

	        $dayname()
	                for the current day, returns the
	                day name ("Sunday", "Monday", ...)
	        $dayname(n)
	                for the given day number (1-7), returns the
	                day name ("Sunday", "Monday", ...)

	        $dirname(a)
	                for the path a, strips off the terminating filename
	                and returns the directory portion

	        $dquote()
	                returns the double quote (") char

	        $err()
	                returns "<<ERR>>"

	        $filemode(a)
	                for path a, returns its mode in octal format

	        $fixed(n)
	        $str(n)
	        $string(n)
	        $text(n)
	                returns #trunc(n) as a string with no decimal places
	        $fixed(n1,n2)
	        $str(n1,n2)
	        $string(n1,n2)
	        $text(n1,n2)
	                returns n1 as as string with n2 decimal places

	        $grname(a)
	        $grname(n)
	        $groupname(a)
	        $groupname(n)
	                for group name a, or gid n, returns its group name
	                (as recorded in the system group file)

	        $grpassword(a)
	        $grpassword(n)
	        $grouppassword(a)
	        $grouppassword(n)
	                for group name a, or gid n, returns its group password
	                (as recorded in the system group file)

	        $grmem(a)
	        $grmem(n)
	        $groupmem(a)
	        $groupmem(n)
	        $grmembers(a)
	        $grmembers(n)
	        $groupmembers(a)
	        $groupmembers(n)
	                for group name a, or gid n, returns its group members
	                (as recorded in the system group file)

	        $hostname()
	                returns the system hostname

	        $if(x,a1,a2)
	                returns a1, if x is TRUE; else a2, if x is FALSE
	        $if(x,a)
	                returns a, if x is TRUE; else the string "<<NIL>>",
	                if x is false

	        $inlin()
	        $inline()
	        $inputline()
	                returns the current input line (without the trailing
	                line-feed; also available as $inlin, $inline, and
	                $inputline)

	        $left(a,n)
	                returns the n left-most chars in a

	        $level()
	                returns the current alarm's level (EMERG, ALERT,
	                CRIT, ERR, WARNING, NOTICE, INFO, DEBUG)

	        $lower(a)
	                returns a all in lower-case

	        $ltrim(a)
	                returns a stripped of any leading (leftmost) whitespace

	        $mid()
	        $middle()
	                [see $substring()]

	        $monthname()
	                for the current month, returns the
	                month name ("January", "February", ...)
	        $monthname(n)
	                for the given month number (1-12), returns the
	                month name ("January", "February", ...)

	        $newline()
	                returns the newline ('\n') char

	        $nil()
	                returns "<<NIL>>"

	        $passwdname()
	                [see $pwname()]

	        $passwdpassword()
	                [see $pwpassword()]

	        $passwdgecos()
	        $passwdcomment()
	                [see $pwgecos()]

	        $passwddir()
	                [see $pwdir()]

	        $passwdshell()
	                [see $pwshell()]

	        $proper(a)
	                returns a with every word capitalized

	        $pwname(a)
	        $pwname(n)
	        $passwdname(a)
	        $passwdname(n)
	                for user name a, or uid n, returns its user name
	                (as recorded in the system passwd file)

	        $pwpassword(a)
	        $pwpassword(n)
	        $passwdpassword(a)
	        $passwdpassword(n)
	                for user name a, or uid n, returns its password
	                (as recorded in the system passwd file)

	        $pwgecos(a)
	        $pwgecos(n)
	        $passwdgecos(a)
	        $passwdgecos(n)
	        $pwcomment(a)
	        $pwcomment(n)
	        $passwdcomment(a)
	        $passwdcomment(n)
	                for user name a, or uid n, returns its gecos/comment
	                (as recorded in the system passwd file)

	        $pwdir(a)
	        $pwdir(n)
	        $passwddir(a)
	        $passwddir(n)
	                for user name a, or uid n, returns its home directory
	                (as recorded in the system passwd file)

	        $pwshell(a)
	        $pwshell(n)
	        $passwdshell(a)
	        $passwdshell(n)
	                for user name a, or uid n, returns its login shell
	                (as recorded in the system passwd file)

	        $repeat(a,n)
	                returns a repeated n times

	        $replace(a1,a2,n1,n2)
	                in the string a1, replace the chars from index position
	                n1 through n2 with the string a2

	        $reverse(a)
	                returns string a with its chars reversed

	        $right(a,n)
	                returns the n right-most chars in a

	        $rtrim(a)
	                returns a stripped of any trailing (rightmost) whitespace

	        $script()
	                [see $alarm()]

	        $space()
	                returns the space (' ') char

	        $squote()
	                returns the single quote (') char

	        $str()
	        $string()
	                [see $fixed()]

	        $substitute(a1,a2,a3,n)
	                in the string a1, replace the string a2 with the string
	                a3, beginning at index position n
	        $substitute(a1,a2,a3)
	                in the string a1, replace every instance of the string a2
	                with the string a3

	        $substring(a,n1,n2)
	        $substr(a,n1,n2)
	        $middle(a,n1,n2)
	        $mid(a,n1,n2)
	                for string a, return n2 chars starting at index position n1
	        $substring(a,n1)
	        $substr(a,n1)
	        $middle(a,n1)
	        $mid(a,n1)
	                for string a, return all chars to the end of the string
	                starting at index position n1

	        $tab()
	                returns the tab ('\t') char

	        $task()
	                returns the current alarm's task description

	        $text()
	                [see $fixed()]

	        $trim(a)
	                returns a stripped of any leading and/or trailing
	                whitespace (spaces or tabs)

	        $upper(a)
	                returns a all in upper-case
	

Flow Control

Pikt scripts run from top to bottom, from beginning to end, unless redirected by one of the flow control constructs.  Pikt comes with a panoply of flow control structures, most usual, and a few not so usual.  ("The usual" below means that the structure follows the usual C-like or Perl-like behavior.)

	        STRUCTURE              DESCRIPTION

	        if-elif-else-endif     the usual (note that you can use elsif and
	                               elseif, too; also, fi instead of endif)
	        while-endwhile         the usual
	        repeat-until           like a do-while loop, except keep looping
	                               until the exit condition is true (no do-
	                               while, because the "do" keyword is reserved
	                               for another use)
	        for-endfor             the usual
	        for-in-endfor          loop through all the keys in the given
	                               associative array
	        foreach-endforeach     loop through all the keys in the given
	                               associative array
	        break                  the usual
	        continue               the usual (note that you can use cont, too)
	        switch-case-default-   the usual
	        breakswitch-           (note that you can use breaksw
	        endswitch              and endsw, too)
	        again                  repeat the current rule
	        leave                  leave the current rule and go on to the next
	        redo                   reprocess the current input line
	        next                   go on to the next input line
	        skip n                 skip ahead n input lines, and resume with
	                               the first rule
	        last                   terminate input processing, go to the
	                               end section, if any
	        pause n                pause for n seconds
	        quit                   go on to the next alarm script
	        die "<message>"        abort the program (and log the reason)
	
Statement blocks are indicated by a keyword-keyword combination, for example, if-endif or for-endfor (no { or } here).

Parentheses around conditions are allowed but are unnecessary (and in certain obscure circumstances can even cause syntax errors; it's best to omit them).  Below, the left-hand statement has the same effect as the right-hand:

	        if <cond>                    if (<cond>)
	        elif <cond>                  elif (<cond>)
	        while <cond>                 while (<cond>)
	        until <cond>                 until (<cond>)
	        for <init> <cond> <incr>     for (<init> <cond> <incr>)
	

Statements

In Pikt scripts (and excepting the script/stanza name, in the leftmost column), every statement begins with a statement keyword.  Most you have seen already:

	        KEYWORD                      ATTRIBUTE

	        init
	        begin
	        rule
	        end

	        status                       active|inactive
	        level                        emergency|alert|critical|error|
	                                     warning|notice|info|debug
	        task                         "<alarm description>"
	        input                        proc|process "<process>"
	                                     file "<filename>"
	                                     logfile "<filename>"
	        filter                       "<process>"
	        seps|separators              "<separators>"
	        dat|data                     <data specifier>
	        keys                         <var> [<var> ...]

	        if                           <conds>
	        elif|elsif|elseif            <conds>
	        endif|fi
	        while                        <conds>
	        endwhile
	        repeat
	        until                        <conds>
	        for                          <set statement> <cond> <set statement>
	                                     <string var> in <keys function>
	        endfor
	        foreach                      <keys function>
	        endforeach
	        break
	        continue|cont
	        switch                       <string or numerical expression>
	        case                         <character or number>
	        default
	        breakswitch|breaksw
	        endswitch|endsw
	        again
	        leave
	        redo
	        next
	        skip                         <numerical expression>
	        last
	        pause                        <numerical expression>
	        quit
	        die                          "<message>"
	
Here are the rest of the statement keywords:
	        KEYWORD                      ATTRIBUTE

	        do                           <function call>
	        set                          <var> = <expr>
	        unset|delete                 <var[]>
	        exec [wait]                  "<process>"
	        output                       [mail "<message line>"]
	                                     [print "<message line>"]
	                                     ["<logfile>"|piktlog|syslog
	                                      "<message line>"]
	
"do" is akin to a nullop.  You use it when when you want to make a function call for its side effects (e.g., when opening a file with #fopen() or splitting a string with #split()) and you don't care about the return value (but maybe you should!).

"set" is for assigning values (either string or numerical expressions) to variables.  Performing a set on a dat variable is illegal (input data assigned to dat variables cannot be overridden).  Illegal, too, is performing a set on a filehandle (e.g., FOO), preceding variable (e.g., @foo), and previous variable (e.g., %foo); once assigned (in the preceding/previous case by Pikt itself internally), these values may not be changed.  An exception is a file handle opened then closed; it may then be reassigned to with a subsequent open function.

A special form of the set action appears in a for statement.  For example,

	        for #i=0 #i<10 #i+=1
	
The "#i=0" and #i+=1" are implied set actions, but in this context the set keyword is neither necessary nor allowed.  To repeat: parentheses, however, are.  This is legal:
	        for (#i=0 #i<10 #i+=1)
	
Use whatever form you're comfortable with (although using parentheses can, in unusual circumstances, be problematic; it's best not to use them).

The 'unset' keyword (aka 'delete') removes an element from an associative array, for example:

	        unset $shell["daemon"]
	
or, equivalently:
	        delete $shell["daemon"]
	
"exec" is for executing a command string when you don't care about the return value (otherwise you could use "do #system()" or "do $command()").  "exec wait" temporarily halts the script run until the exec'ed command finishes.  You would normally use exec wait, resorting to just plain exec for unusually time-consuming exec'ed commands.

The "output" statement is akin to other language's "print".  When used as

	        output "<message line>"
	
the text string is sent to the user screen (stdout).  Usually, output is sent as e-mail using the statement
	        output mail "<message line>"
	
Print output, or output to a logfile, is achieved by inserting the keyword "print", or the appropriate log keyword after the "output".  "output piktlog" outputs to the current alert's log file (e.g., Urgent.log), "output syslog" logs to the system log (syslog), and "output log "<logfile>"" logs to the special log file specified

In Pikt, you achieve the functionality of other language's printf() by means of the string formatting functions.

When run non-interactively (i.e., when called without human intervention by piktd), or when run at the command line but with either the +M or +L options, pikt queues mail and print message lines until the end of its program run, only sending out e-mail or dumping to a printer just before pikt terminates.  In "log" statements, on the other hand, the message line is logged to the current alert log, to syslog, or the special log file immediately.

There are no practical limits to the number of Pikt script statements, nor to the number of rules.  There can be only one init, begin and end section, though.

Regular Expressions

Pikt regular expressions follow the usual regular expression rules with any necessary clarifications/amplifications to follow.

Here are the regular expression operators:

	        OPERATOR           MEANING

	        a =~ b             string b matches at least one substring within a
	        a =~~ b            like the above, but without case sensitivity
	        a !~ b             string b matches no substring within a
	        a !~~ b            like the above, but without case sensitivity
	
For example, all of the following are true:
	        "this is a test" =~ "is"
	        "this is a test" =~~ "IS"
	        "this is a test" !~ "THIS"
	        "this is a test" !~~ "that"

	        "this is a test" =~ ""
	        "" !~ "this is a test"
	
These characters have special meaning within Pikt regular expressions:
	        CHARACTER(S)    MEANING

	        .               matches any single character
	        *               matches zero or more instances of the preceding
	                        character/pattern
	        ?               matches zero or one instance(s) of the preceding
	                        character/pattern
	        +               matches one or more instances of the preceding
	                        character/pattern
	        {m,n}           matches as few as m, or as many as n, instances
	                        of the preceding character/pattern

	        ( )             enclose a subexpression, or set of subexpressions
	                        separated by |
	        |               separates subexpressions (think of "or")
	        [ ]             enclose a set of characters/character ranges
	        ^               as the first character in a [ ] subexpression,
	                        indicates set negation; as the first character
	                        in a regular expression, anchors to the
	                        beginning of the string expression on the
	                        left-hand side of the regexp operator
	        $               anchors to the end of the string expression
	                        on the left-hand side of the regexp operator
	
In addition to user-specified character classes, Pikt supports these built-in predefined character classes:
	        [[:alnum:]]     the set of alphanumeric characters
	        [[:alpha:]]     the set of letters
	        [[:blank:]]     tab and space
	        [[:cntrl:]]     the control characters
	        [[:digit:]]     the decimal digits
	        [[:graph:]]     the printable characters except space
	        [[:lower:]]     the lower-case letters
	        [[:print:]]     the printable characters
	        [[:punct:]]     the punctuation characters
	        [[:space:]]     whitespace characters
	        [[:upper:]]     the upper-case letters
	
Backslash escapes suppress a character's specialness.  So, "\\*" is a literal asterisk, and the following are all true:
	        "fo*bar" !~ "fo*bar"        // left side literal string,
	                                    // right side regexp
	        "fo*bar" !~ "fo\*bar"

	        "fo*bar" =~ "fo\\*bar"

	        "fo*bar" =~ "\\*"

	        "*" =~ "\\*"
	
In any of the above left-hand expressions, you could substitute "fo\*bar", and the statements would all still be true.

Usually, just a single backslash is required for this purpose.  In Pikt, however, backslashes are a general escape character.  If, for example, you want to output the literal text string "$x" without the $x being interpreted as a variable (which Pikt would attempt to resolve to a value), you would use "\$x".  So, if you require a backslash in the final product, you must supply double backslashes going in.  Again, see the sample config files for examples of double-backslash usage.

Note that every time a regular expression containing matching parentheses is invoked, for example in any of the following situations

	        dat "([^:]*):([^:]*)"

	        if $line =~ "^([^:]*):([^:]*)"

	        do #split($rdline, "([^:]*):([^:]*)")
	
you can reference the first parentheses-enclosed matched subexpression with $1, the second with $2, and so on.  $0 references the entire matched subexpression.

Note well:  The $0, $1, and so on only persist until the next regexp pattern match.  The next time you use =~ (or any of the other regexp operators), or the next time you invoke the #split() function (in any of its forms), any previous $0, $1, ... values get supplanted by the values in the latest regexp.  You will encounter many strange bugs unless you keep this in mind!

Alternate forms for referencing regexp matches are: $[0], $[1], $[2], and so on.  These make it possible to reference the matched expressions within for loops:

	        set #n = #split($rdlin)
	        for #i=1 #i<=#n #i+=1
	                output $[#i]
	        endfor
	
Here is a technique for saving $0, $1, ... before a subsequent regexp action:
	        set #n = #split($rdlin)
	        for #i=1 #i<=#n #i+=1
	                set $f[#i] = $[#i]
	        endfor
	        ...
	        if $f[3] =~ "cantata|sonata|toccata"    // wipes out $3 & $[3] value
	                output $f[3]
	        fi
	
Better still is to use the #split() function (with all three arguments required) this way:
	        do #split($f, $rdlin, " ")
	        ...
	        if $f[3] =~ "cantata|sonata|toccata"    // wipes out $3 & $[3] value
	                output $f[3]
	        fi
	
If you failed to save the previous regexp values in the $f[] array and simply referenced $3 or $[3], that value would be undefined, since in the =~ test you didn't put ( )'s around any third subexpression, but even if you did (around "toccata") you have lost your previous $3 value.

For further coverage of regular expressions, see the GNU RX info pages.



Home | News | Introduction | Samples | Tutorial | Reference | Software | Authors | Licensing
Forum | Marketplace | Links | SiteSearch | FAQ | Contribute | Donate | ContactUs
Top of Page

Join pikt-users, pikt-workers, and/or the PIKT Forum. 
Open Hand Please visit our sponsors.

Page best viewed at 1024x768.   Page last updated 2005-01-07.
This site is PIKT® powered.
PIKT® is a registered trademark of the University of Chicago.
Copyright © 1998-2005 Robert Osterlund.  All rights reserved.