zeek/doc/script-reference/statements.rst
Tim Wojtulewicz ded98cd373 Copy docs into Zeek repo directly
This is based on commit 2731def9159247e6da8a3191783c89683363689c from the
zeek-docs repo.
2025-09-26 02:58:29 +00:00

1237 lines
36 KiB
ReStructuredText
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Declarations and Statements
===========================
The Zeek scripting language supports the following declarations_ and
statements_.
Declarations
------------
Declarations cannot occur within a function, hook, or event handler.
Declarations must appear before any statements (except those statements
that are in a function, hook, or event handler) in the concatenation of
all loaded Zeek scripts.
.. list-table::
:header-rows: 1
* - Name
- Description
* - :zeek:keyword:`module`
- Change the current module
* - :zeek:keyword:`export`
- Export identifiers from the current module
* - :zeek:keyword:`global`
- Declare a global variable
* - :zeek:keyword:`const`
- Declare a constant
* - :zeek:keyword:`option`
- Declare a configuration option
* - :zeek:keyword:`type`
- Declare a user-defined type
* - :zeek:keyword:`redef`
- Redefine a global value or extend a user-defined type
* - `Callables`_: :zeek:type:`function`, :zeek:type:`event`, :zeek:type:`hook`
- Declare a function, event handler, or hook
.. zeek:keyword:: module
module
~~~~~~
The ``module`` keyword is used to change the current module. This
affects the scope of any subsequently declared global identifiers.
Example:
.. code-block:: zeek
module mymodule;
If a global identifier is declared after a ``module`` declaration,
then its scope ends at the end of the current Zeek script or at the
next ``module`` declaration, whichever comes first. However, if a
global identifier is declared after a ``module`` declaration, but inside
an :zeek:keyword:`export` block, then its scope ends at the end of the
last loaded Zeek script, but it must be referenced using the namespace
operator (``::``) in other modules.
There can be any number of ``module`` declarations in a Zeek script.
The same ``module`` declaration can appear in any number of different
Zeek scripts.
The reserved module name ``GLOBAL`` switches to the default global
namespace. This comes in handy if you're working in a module context but want to
define something globally, without the module's namespacing. For example, the
:ref:`Notice Framework <notice-framework>` uses this approach to define the
``NOTICE()`` function.
.. zeek:keyword:: export
export
~~~~~~
An ``export`` block contains one or more declarations
(no statements are allowed in an ``export`` block) that the current
module is exporting. This enables these global identifiers to be visible
in other modules (but not prior to their declaration) via the namespace
operator (``::``). See the :zeek:keyword:`module` keyword for a more
detailed explanation.
Example:
.. code-block:: zeek
export {
redef enum Log::ID += { LOG };
type Info: record {
ts: time &log;
uid: string &log;
};
const conntime = 30sec &redef;
}
Note that the braces in an ``export`` block are always required
(they do not indicate a compound statement). Also, no semicolon is
needed to terminate an ``export`` block.
.. zeek:keyword:: global
global
~~~~~~
Variables declared with the ``global`` keyword will have global scope.
If a type is not specified, then an initializer is required so that
the type can be inferred. Likewise, if an initializer is not supplied,
then the type must be specified. In some cases, when the type cannot
be correctly inferred, the type must be specified even when an
initializer is present. Example:
.. code-block:: zeek
global pi = 3.14;
global hosts: set[addr];
global ciphers: table[string] of string = table();
Variable declarations outside of any function, hook, or event handler are
required to use this keyword (unless they are declared with the
:zeek:keyword:`const` keyword instead).
Definitions of functions, hooks, and event handlers are not allowed
to use the ``global`` keyword. However, function declarations (i.e., no
function body is provided) can use the ``global`` keyword.
The scope of a global variable begins where the declaration is located,
and extends through all remaining Zeek scripts that are loaded (however,
see the :zeek:keyword:`module` keyword for an explanation of how modules
change the visibility of global identifiers).
.. zeek:keyword:: const
const
~~~~~
A variable declared with the ``const`` keyword cannot be changed by
reassignment. Variables declared as constant are required to be initialized at
the time of declaration. Normally, the type is inferred from the initializer,
but the type can be explicitly specified. Example:
.. code-block:: zeek
const pi = 3.14;
const ssh_port: port = 22/tcp;
The value of a constant cannot be changed:
.. code-block:: zeek
ssh_port = 80/tcp; # "error [...]: const is not a modifiable lvalue (ssh_port)"
The only exception is if the variable is a global constant and has the
:zeek:attr:`&redef` attribute, but even then its value can be changed only with
a :zeek:keyword:`redef` declaration:
.. code-block:: zeek
const ssh_port: port = 22/tcp &redef;
# ...
redef ssh_port = 2222/tcp; # ok
Const-ness does not apply to members of existing container type instances, which
can still be modified, added, or removed:
.. code-block:: zeek
const ssh_ports = vector(22/tcp, 2222/tcp);
# ...
ssh_ports += 222/tcp; # ok
ssh_ports = vector(222/tcp); # error [...]: const is not a modifiable lvalue (ssh_ports)
The scope of a constant is local if the declaration is in a
function, hook, or event handler, and global otherwise.
Note that the ``const`` keyword cannot be used with either the ``local``
or ``global`` keywords (i.e., ``const`` is an alternative to either
``local`` or ``global``).
.. zeek:keyword:: option
option
~~~~~~
A variable declared with the ``option`` keyword is a configuration option.
Options are required to be initialized at the
time of declaration. Normally, the type is inferred from the initializer,
but the type can be explicitly specified. Example:
.. code-block:: zeek
option hostname = "host-1";
option peers: set[addr] = {};
The initial value can be redefined with a :zeek:keyword:`redef`.
The value of an option cannot be changed by an assignment statement, but
it can be changed by either the :zeek:id:`Config::set_value` function or
by changing a config file specified in :zeek:id:`Config::config_files`.
The scope of an option is global.
Note that an ``option`` declaration cannot also use the ``local``,
``global``, or ``const`` keywords.
.. zeek:keyword:: type
type
~~~~
The ``type`` keyword is used to declare a user-defined type. The name
of this new type has global scope and can be used anywhere a built-in
type name can occur.
The ``type`` keyword is most commonly used when defining a
:zeek:type:`record` or an :zeek:type:`enum`, but is also useful when
dealing with more complex types.
Example:
.. code-block:: zeek
type mytype: table[count] of table[addr, port] of string;
global myvar: mytype;
.. zeek:keyword:: redef
redef
~~~~~
There are several ways that ``redef`` can be used: to redefine the initial
value of a global variable or runtime option, to extend a record type or
enum type, to add or remove attributes of record fields, or to specify a
new event handler body that replaces all those that were previously defined.
Redefining Initial Values
^^^^^^^^^^^^^^^^^^^^^^^^^
If you're using ``redef`` to redefine the initial value of a global variable
(defined using either :zeek:keyword:`const` or :zeek:keyword:`global`), then
the variable that you want to change must have the :zeek:attr:`&redef`
attribute. You can use ``redef`` to redefine the initial value of a
runtime option (defined using :zeek:keyword:`option`) even if it doesn't
have the :zeek:attr:`&redef` attribute.
If the variable you're changing is a table, set, vector, or pattern, you can
use ``+=`` to add new elements, or you can use ``=`` to specify a new value
(all previous contents of the object are removed). If the variable you're
changing is a set or table, then you can use the ``-=`` operator to remove
the specified elements (nothing happens for specified elements that don't
exist). If the variable you are changing is not a table, set, or pattern,
then you must use the ``=`` operator.
Examples:
.. code-block:: zeek
redef pi = 3.14;
redef set_of_ports += { 22/tcp, 53/udp };
Extending Records Types or Enums
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you're using ``redef`` to extend a record or enum, then you must
use the ``+=`` assignment operator.
For an enum, you can add more enumeration constants, and for a record
you can add more record fields (however, each record field in the ``redef``
must have either the :zeek:attr:`&optional` or :zeek:attr:`&default`
attribute).
Examples:
.. code-block:: zeek
redef enum color += { Blue, Red };
redef record MyRecord += { n2:int &optional; s2:string &optional; };
Changing Attributes of Record Fields
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 5.1
If you're using ``redef`` to change the attributes of a record field, you must
use either the ``+=`` or ``-=`` assignment operator and specify the record field
by means of the field access operator ``$`` using the record type's name.
Only the ``&log`` attribute can currently be removed from or added to an
existing record field. This enables removal of columns from logs that are
uninteresting for a given deployment or include columns that do not yet have
the ``&log`` attribute.
.. note::
The :ref:`logging framework <framework-logging>` provides a separate
mechanism to exclude columns from logs by means of the ``exclude`` field
on :zeek:see:`Log::Filter` instances.
Examples:
.. code-block:: zeek
redef record Notice::Info$email_dest -= { &log }
redef record X509::Certificate$tbs_sig_alg += { &log };
Replacing Event Handlers
^^^^^^^^^^^^^^^^^^^^^^^^
If you're using ``redef`` to specify a new event handler body that
replaces all those that were previously defined (i.e., any subsequently
defined event handler body will not be affected by this ``redef``), then
the syntax is the same as a regular event handler definition except for
the presence of the ``redef`` keyword.
Example:
.. code-block:: zeek
redef event myevent(s:string) { print "Redefined", s; }
.. _function/event/hook:
Callables
~~~~~~~~~
Callable types come in three flavors: :zeek:type:`function`, :zeek:type:`event`
handler, and :zeek:type:`hook`. All come with associated arguments and
bodies of statements. The following table compares and contrasts:
.. list-table::
:header-rows: 1
* - **Features**
- :zeek:type:`function`
- :zeek:type:`hook`
- :zeek:type:`event`
* - **Anonymity**
- Yes
- No
- No
* - **Multiple bodies and priorities**
- No
- Yes
- Yes
* - **Immediate invocation**
- Yes
- Yes
- No
* - **Scheduling**
- No
- No
- Yes
* - **Default arguments**
- Yes
- Yes
- Yes
* - **Container argument mutability**
- Yes if synchronous, no if :ref:`asynchronous <asynchronous-return>`
- Yes
- Yes
* - **Alternate declarations**
- No
- Yes
- Yes
* - **Return value**
- Yes
- Yes
- No
Anonymity
^^^^^^^^^
While Zeek does support the concept of :ref:`anonymous functions
<anonymous-function>` (i.e., lambdas), hooks and events cannot be
anonymous. They are referenced by their names. As an example, reducer functions
in the :ref:`SumStats framework <sumstats-framework>` are often implemented as
lambda functions.
Multiple bodies and priorities
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Functions cannot have multiple bodies, however, hooks and events can. This means
that different scripts can add additional bodies to a hook or event associated
with a unique name. When an event or hook is executed, Zeek needs a way to order
the execution. This is accomplished with the numerical :zeek:attr:`&priority`
attribute: by default, a hooks or events body has a priority of zero, but any
integer-range value is valid.
Immediate invocation
^^^^^^^^^^^^^^^^^^^^
Functions and hook bodies are executed immediately. That means if a script is
being interpreted and a line contains a function call, execution flow is
immediately passed to that function (or hook). This does not happen for
events. Events are pushed onto an event queue within Zeek and are handled as
time passes.
Scheduling
^^^^^^^^^^
Functions and hooks cannot be scheduled like events can. Scheduling places an
event onto the event queue and is the equivalent to immediately invoking a
function or hook. Attempting to schedule a function or a hook results in the
same syntax error: "function invoked as an event".
Default arguments
^^^^^^^^^^^^^^^^^
Functions, hooks, and events all support :ref:`default values <default-values>`
for their arguments.
Container argument mutability
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When argument types are container types (such as records or tables), mutating the
arguments within the body of a function, hook, or event causes the argument to
retain that mutation: container types are passed by reference while atomic types
are passed by value.
Asynchronous functions are an exception: the evaluation of :zeek:keyword:`when`
statements invokes such functions with copies of their arguments, causing
modifications made inside the asynchronous function to be lost. Please refer to
:ref:`asynchronous return <asynchronous-return>` for possible workarounds.
Alternate declarations
^^^^^^^^^^^^^^^^^^^^^^
Hooks and events do support alternate prototype declarations. This means that a
set or scripts may define a single event (or hook) name multiple times with
different argument sets. This is often referred to as overloading in other
languages. Functions do not support alternate prototype declarations.
Return value
^^^^^^^^^^^^
All functions must return a value. However, functions with no explicit return
type implicitly return void. This can seem a bit odd as void isnt a valid Zeek
type. A hook body is allowed to return before it breaks. Hooks may return
either a boolean type or void, but arent required to return any value. Events
cannot return a value because they are scheduled through the event loop and
dont have a caller to return to.
For further details on how to declare callables, see the :zeek:type:`function`,
:zeek:type:`event` handler, and :zeek:type:`hook` documentation.
Statements
----------
Statements (except those contained within a function, hook, or event
handler) can appear only after all global declarations in the concatenation
of all loaded Zeek scripts.
Each statement in a Zeek script must be terminated with a semicolon (with a
few exceptions noted below). An individual statement can span multiple
lines.
Here are the statements that the Zeek scripting language supports.
.. list-table::
:header-rows: 1
* - Name
- Description
* - :zeek:keyword:`local`
- Declare a local variable
* - :zeek:keyword:`add`, :zeek:keyword:`delete`
- Add or delete elements
* - :zeek:keyword:`assert`
- Runtime assertion
* - :zeek:keyword:`print`
- Print to stdout or a file
* - :zeek:keyword:`for`, :zeek:keyword:`while`,
:zeek:keyword:`next`, :zeek:keyword:`break`
- Loop over each element in a container object (``for``), or as long as a
condition evaluates to true (``while``).
* - :zeek:keyword:`if`
- Evaluate boolean and if true, execute a statement
* - :zeek:keyword:`switch`, :zeek:keyword:`break`, :zeek:keyword:`fallthrough`
- Evaluate expression and execute statement with a matching value
* - :zeek:keyword:`when`
- Asynchronous execution
* - :zeek:keyword:`event`, :zeek:keyword:`schedule`
- Invoke or schedule an event handler
* - :zeek:keyword:`return`
- Return from function, hook, or event handler
.. zeek:keyword:: add
add
~~~
The ``add`` statement is used to add an element to a :zeek:type:`set`.
Nothing happens if the specified element already exists in the set.
Example:
.. code-block:: zeek
local myset: set[string];
add myset["test"];
.. zeek:keyword:: assert
assert
~~~~~~
.. versionadded:: 6.1
The ``assert`` statement can be used for runtime assertion checks or as a building
block for a testing framework. It takes an expression ``expr`` of type
:zeek:see:`bool` and an optional message of type :zeek:see:`string`.
If ``expr`` at runtime evaluates to ``F``, the string representation
of the expression and the given message, if any, are logged
via :zeek:see:`Reporter::error` by default.
Script execution for a given event handler stops with a failing ``assert`` statement
comparable to a scripting runtime error after generating the log.
Example:
.. literalinclude:: assert_1.zeek
:language: zeek
:linenos:
:tab-width: 4
This script prints the following messages to stderr, as well as logging them to
:file:`reporter.log`.
.. code-block:: console
$ zeek assert_1.zeek
error in ./assert_1.zeek, line 6: assertion failure: 40 < x
error in ./assert_1.zeek, line 12: assertion failure: 40 < x (37 is not greater than 40)
.. note::
Zeek's exit code in this example will be ``0``, indicating success.
Script errors other than those in a ``zeek_init()`` handler are not
reflected in Zeek's exit code.
The logging behavior of failing assert statements can be customized using the
:zeek:see:`assertion_failure` or :zeek:see:`assertion_result` hook.
Using the :zeek:see:`break` statement in either hook allows for suppression
of the the default log generation.
The :zeek:see:`assertion_result` hook is targeted for testing frameworks as it
is likely prohibitively expensive for use in a live production environment due
to being invoked for every ``assert`` statement execution.
.. zeek:keyword:: break
break
~~~~~
The ``break`` statement is used to break out of a :zeek:keyword:`switch`,
:zeek:keyword:`for`, or :zeek:keyword:`while` statement.
.. zeek:keyword:: delete
delete
~~~~~~
The ``delete`` statement is used to remove an element from a
:zeek:type:`set` or :zeek:type:`table`, or to remove a value from
a :zeek:type:`record` field that has the :zeek:attr:`&optional` attribute.
When attempting to remove an element from a set or table,
nothing happens if the specified index does not exist.
When attempting to remove a value from an ``&optional`` record field,
nothing happens if that field doesn't have a value.
Example:
.. code-block:: zeek
local myset = set("this", "test");
local mytable = table(["key1"] = 80/tcp, ["key2"] = 53/udp);
local myrec = MyRecordType($a = 1, $b = 2);
delete myset["test"];
delete mytable["key1"];
# In this example, "b" must have the "&optional" attribute
delete myrec$b;
.. versionadded:: 7.0
The ``delete`` statement can also be used to remove all elements from
a :zeek:type:`set`, :zeek:type:`table` or :zeek:type:`vector`.
Example:
.. code-block:: zeek
local myset = set("this", "test");
delete myset;
.. zeek:keyword:: event
event
~~~~~
The ``event`` statement immediately queues invocation of an event handler.
Example:
.. code-block:: zeek
event myevent("test", 5);
.. zeek:keyword:: fallthrough
fallthrough
~~~~~~~~~~~
The ``fallthrough`` statement can be used within a ``case`` block to
indicate that execution should continue at the next ``case`` or ``default``
label.
For an example, see the :zeek:keyword:`switch` statement.
.. zeek:keyword:: for
for
~~~
A ``for`` loop iterates over each element in a string, set, vector, or
table and executes a statement for each iteration (note that the order
in which the loop iterates over the elements in a set or a table is
nondeterministic). However, no loop iterations occur if the string,
set, vector, or table is empty.
For each iteration of the loop, a loop variable will be assigned to an
element if the expression evaluates to a string or set, or an index if
the expression evaluates to a vector or table. Then the statement
is executed.
If the expression is a table or a set with more than one index, then the
loop variable must be specified as a comma-separated list of different
loop variables (one for each index), enclosed in brackets.
If the expression is a table, keys and values can be iterated over at the
same time by specifying a key and value variable. Similarly, if the expression
is a vector, indices and values can be iterated over at the same time by
specifying an index and value variable.
The core exposes value variables for free, so this should be preferred to
accessing the values in a separate lookup inside the loop.
Note that the loop variable in a ``for`` statement is not allowed to be
a global variable, and it does not need to be declared prior to the ``for``
statement. The type will be inferred from the elements of the
expression.
In some scenarios, the loop variable or parts of the table index may be
unused in the ``for`` loop's body. Zeek reserves ``_``, the blank identifier, as
an explicitly way to support capturing unused variables.
The blank identifier can be assigned expressions of any type, but it can
never be referenced.
For the ``for`` loop, this allows to use the blank identifier to capture
unused loop variables of differing types---something that isn't possible
with normal variables.
As a special case, all index variables of a table or set can be ignored with
a single blank identifier.
In fact, in current versions of Zeek, ignoring all index variables allows
for faster iteration and is therefore recommended to be used when possible.
Currently, modifying a container's membership while iterating over it may
result in undefined behavior, so do not add or remove elements
inside the loop.
A :zeek:keyword:`break` statement will immediately terminate the ``for``
loop, and a :zeek:keyword:`next` statement will skip to the next loop
iteration.
Example:
.. code-block:: zeek
local myset = set(80/tcp, 81/tcp);
local mytable = table([ 10.0.0.1, 80/tcp ] = "s1", [ 10.0.0.2, 81/tcp ] = "s2");
local myvector = vector("zero", "one", "two");
for ( p in myset )
print p;
for ( [ip, p], val in mytable )
{
if ( val == "done" )
break;
if ( val == "skip" )
next;
print ip, p;
}
for ( _, val in mytable )
print val;
for ( [ip, _], _ in mytable )
print ip;
for ( i, val in myvector )
print i, val;
for ( _, val in myvector )
print val;
.. zeek:keyword:: if
if
~~
Evaluates a given expression, which must yield a :zeek:type:`bool` value.
If true, then a specified statement is executed. If false, then
the statement is not executed. Example:
.. code-block:: zeek
if ( x == 2 ) print "x is 2";
However, if the expression evaluates to false and if an ``else`` is
provided, then the statement following the ``else`` is executed. Example:
.. code-block:: zeek
if ( x == 2 )
print "x is 2";
else
print "x is not 2";
.. zeek:keyword:: local
local
~~~~~
A variable declared with the ``local`` keyword will be local. If a type
is not specified, then an initializer is required so that the type can
be inferred. Likewise, if an initializer is not supplied, then the
type must be specified.
Examples:
.. code-block:: zeek
local x1 = 5.7;
local x2: double;
local x3: double = 5.7;
Variable declarations inside a function, hook, or event handler are
required to use this keyword (the only two exceptions are variables
declared with :zeek:keyword:`const`, and variables implicitly declared in a
:zeek:keyword:`for` statement).
The scope of a local variable starts at the location where it is declared
and persists to the end of the function, hook,
or event handler in which it is declared (this is true even if the
local variable was declared within a `compound statement`_ or is the loop
variable in a ``for`` statement).
.. zeek:keyword:: next
next
~~~~
The ``next`` statement can only appear within a :zeek:keyword:`for` or
:zeek:keyword:`while` loop. It causes execution to skip to the next
iteration.
.. zeek:keyword:: print
print
~~~~~
The ``print`` statement takes a comma-separated list of one or more
expressions. Each expression in the list is evaluated and then converted
to a string. Then each string is printed, with each string separated by
a comma in the output.
Examples:
.. code-block:: zeek
print 3.14;
print "Results", x, y;
By default, the ``print`` statement writes to the standard
output (stdout). However, if the first expression is of type
:zeek:type:`file`, then ``print`` writes to that file.
If a string contains non-printable characters (i.e., byte values that are
not in the range 32 - 126), then the ``print`` statement converts each
non-printable character to an escape sequence before it is printed.
For more control over how the strings are formatted, see the :zeek:id:`fmt`
function.
.. zeek:keyword:: return
return
~~~~~~
The ``return`` statement immediately exits the current function, hook, or
event handler. For a function, the specified expression (if any) is
evaluated and returned. A ``return`` statement in a hook or event handler
cannot return a value because event handlers and hooks do not have
return types.
Examples:
.. code-block:: zeek
function my_func(): string
{
return "done";
}
event my_event(n: count)
{
if ( n == 0 ) return;
print n;
}
.. _asynchronous-return:
Asynchronous return
^^^^^^^^^^^^^^^^^^^
There is a special form of the ``return`` statement that is only allowed
in functions. Syntactically, it looks like a :zeek:keyword:`when` statement
immediately preceded by the ``return`` keyword. This form of the ``return``
statement is used to specify a function that delays its result: an
*asynchronous function*.
Such functions can only be called in the expression of a :zeek:keyword:`when`
statement). The function returns at the time the ``when``
statement's condition becomes true, and the function returns the value
that the ``when`` statement's body returns (or if the condition does
not become true within the specified timeout interval, then the function
returns the value that the ``timeout`` block returns).
(Note that if you use the deprecated feature of not listing the *captures*
in your ``return when`` statement, then, in contrast to regular functions, your
asynchronous functions cannot make lasting modifications to
arguments that have aggregate types, because those values will be
deep-copied upon execution of the ``return when``.)
Example:
.. code-block:: zeek
global X: table[string] of count;
function a() : count
{
# This delays until condition becomes true.
return when ( "a" in X )
{
return X["a"];
}
timeout 30 sec
{
return 0;
}
}
event zeek_init()
{
# Installs a trigger which fires if a() returns 42.
when ( a() == 42 )
print "expected result";
print "Waiting for a() to return...";
X["a"] = 42;
}
.. zeek:keyword:: schedule
schedule
~~~~~~~~
The ``schedule`` statement is used to raise a specified event with
specified parameters at a later time specified as an :zeek:type:`interval`.
Example:
.. code-block:: zeek
schedule 30sec { myevent(x, y, z) };
.. note::
The braces are always required here (that is, they do not indicate a
`compound statement`_). Also, ``schedule`` is actually an expression that
returns a value of type ``timer``, but in practice the return value is not
used.
.. note::
Always specify event names with their full module namespace. For example,
if the above ``myevent`` lives in the ``MyModule`` module, then say the
following even when working inside the module:
.. code-block:: zeek
schedule 30sec { MyModule::myevent(x, y, z) };
See :ref:`event-namespacing-pitfall` for details.
.. note::
Using ``schedule`` within :zeek:see:`zeek_init` does not usually have the
desired behavior -- since :zeek:see:`network_time` is not yet initialized,
the scheduled event may be dispatched upon processing the first network
packet since that will update network-time from zero to the time associated
with capturing that packet. A typical workaround is to ignore the first
time such an event is dispatched and simply re-schedule it or to instead
schedule the first event from within the :zeek:see:`network_time_init` event.
.. zeek:keyword:: switch
switch
~~~~~~
A ``switch`` statement evaluates a given expression and jumps to
the first ``case`` label which contains a matching value (the result of the
expression must be type-compatible with all of the values in all of the
``case`` labels). If there is no matching value, then execution jumps to
the ``default`` label instead, and if there is no ``default`` label then
execution jumps out of the ``switch`` block.
Here is an example (assuming that ``get_day_of_week`` is a
function that returns a string):
.. code-block:: zeek
switch get_day_of_week() {
case "Sa", "Su":
print "weekend";
fallthrough;
case "Mo", "Tu", "We", "Th", "Fr":
print "valid result";
break;
default:
print "invalid result";
break;
}
A ``switch`` block can have any number of ``case`` labels, and one
optional ``default`` label.
A ``case`` label can have a comma-separated list of
more than one value. A value in a ``case`` label can be an expression,
but it must be a constant expression (i.e., the expression can consist
only of constants).
Each ``case`` and the ``default`` block must
end with either a :zeek:keyword:`break`, :zeek:keyword:`fallthrough`, or
:zeek:keyword:`return` statement (although ``return`` is allowed only
if the ``switch`` statement is inside a function, hook, or event handler).
Note that the braces in a ``switch`` statement are always required (these
do not indicate the presence of a `compound statement`_), and that no
semicolon is needed at the end of a ``switch`` statement.
There is an alternative form of the switch statement that supports
switching by type rather than value. This form of the switch statement
uses type-based versions of ``case``:
- ``case type t: ...``: Take branch if the value of the switch expression
could be casted to type ``t`` (where ``t`` is the name of a Zeek script
type, either built-in or user-defined).
- ``case type t as x: ...``: Same as above, but the casted value is
available through ID ``x``.
Multiple types can be listed per branch, separated by commas (the ``type``
keyword must be repeated for each type in the list).
Example:
.. code-block:: zeek
function example(v: any)
{
switch (v) {
case type count as c:
print "It's a count", c;
break;
case type bool, type addr:
print "It's a bool or address";
break;
}
}
Note that a single switch statement switches either by type or by value,
but not both.
Also note that the type-based switch statement will trigger a runtime
error if any cast in any ``case`` is an unsupported cast (see the
documentation of the type casting operator ``as``).
A type-casting ``case`` block is also not allowed to use a
:zeek:keyword:`fallthrough` statement since that could generally mean
entering another type-casting block. That is, the switched-upon value could
get cast to at least two different types, which is not a valid possibility.
.. _when-statement:
.. zeek:keyword:: when
when
~~~~
Evaluates a given expression, which must result in a value of type
:zeek:type:`bool`. When the value of the expression becomes available
and if the result is true, then a specified statement is executed.
In the following example, if the expression evaluates to true, then
the ``print`` statement is executed:
.. code-block:: zeek
when ( (local x = foo()) && x == 42 )
{
print x;
}
However, if a timeout is specified, and if the expression does not
evaluate to true within the specified timeout interval, then the
statement following the ``timeout`` keyword is executed:
.. code-block:: zeek
when ( (local x = foo()) && x == 42 )
{
print x;
}
timeout 5sec
{
print "timeout";
}
Note that when a timeout is specified the braces are
always required (these do not indicate a `compound statement`_).
The expression in a ``when`` statement can contain a declaration of a local
variable but only if the declaration is written in the form
``local *var* = *init*`` (example: ``local x = myfunction()``). This form
of a local declaration is actually an expression, the result of which
is always a boolean true value.
The expression in a ``when`` statement can contain an asynchronous function
call such as :zeek:id:`lookup_hostname` (in fact, this is the only place
such a function can be called), but it can also contain an ordinary
function call. When an asynchronous function call is in the expression,
then Zeek will continue processing statements in the script following
the ``when`` statement, and when the result of the function call is available
Zeek will finish evaluating the expression in the ``when`` statement.
See the :zeek:keyword:`return` statement for an explanation of how to
create an asynchronous function in a Zeek script.
The elements of a ``when`` statement can include references to the local
variables of the function/event/hook body in which they appear (as well
as to global variables). If they do, then you need to specify the locals
variables as *captures*, using ``[...]`` in the same manner as done for
:ref:`anonymous functions <anonymous-function>`. By default captures are
done using *shallow-copying*, behaving like an assignment; you can add the
keyword
``copy`` to instead make a *deep* copy.
For example:
.. code-block:: zeek
type r: record { x: int; y: int; };
global g = r($x=100, $y=100);
event zeek_init()
{
local l = r($x=1, $y=2);
local l2 = r($x=3, $y=4);
when [l, copy l2] ( g$x < 0 )
{
print l, l2;
}
l$x = 10;
l2$x = 20;
}
event zeek_init() &priority=-10
{
g$x = -999;
}
will print ``[x=10, y=2], [x=3, y=4]``, because, as a shallow copy, the
version of ``l`` inside the ``when`` statement will reflect the changes
made to its record after execution of the ``when`` statement; while the
version of ``l2`` will not, since it holds a deep copy of the record
made upon executing the ``when`` statement.
For the captures you need to list all of local variables used in the
statement: those in the initial condition, as well as those appearing in
the body or the ``timeout`` statement. You do not need to list new
``local``'s introduce in the expression (such as ``local x = foo()`` in
the example given earlier above).
It also works, for now, to leave off the captures entirely, but this
form is deprecated. It provides old-style semantics, in which every
local is automatically captured via deep-copy.
.. zeek:keyword:: while
while
~~~~~
A ``while`` loop iterates over a body statement as long as a given
condition remains true.
A :zeek:keyword:`break` statement can be used at any time to immediately
terminate the ``while`` loop, and a :zeek:keyword:`next` statement can be
used to skip to the next loop iteration.
Example:
.. code-block:: zeek
local i = 0;
while ( i < 5 )
print ++i;
while ( some_cond() )
{
local finish_up = F;
if ( skip_ahead() )
next;
if ( finish_up )
break;
}
.. _compound statement:
Compound Statement
~~~~~~~~~~~~~~~~~~
A compound statement is created by wrapping zero or more statements in
braces ``{ }``. Individual statements inside the braces need to be
terminated by a semicolon, but a semicolon is not needed at the end
(outside of the braces) of a compound statement.
A compound statement is required in order to execute more than one
statement in the body of a :zeek:keyword:`for`, :zeek:keyword:`while`,
:zeek:keyword:`if`, or :zeek:keyword:`when` statement.
Example:
.. code-block:: zeek
if ( x == 2 )
{
print "x is 2";
++x;
}
Note that there are other places in the Zeek scripting language that use
braces, but that do not indicate the presence of a compound
statement (these are noted in the documentation).
.. _null statement:
Null Statement
~~~~~~~~~~~~~~
The null statement (executing it has no effect) consists of just a
semicolon. This might be useful during testing or debugging a Zeek script
in places where a statement is required, but it is probably not useful
otherwise.
Example:
.. code-block:: zeek
if ( x == 2 )
;