当前位置: 首页 > 工具软件 > Bro > 使用案例 >

Bro脚本语法4-声明和语句(Declarations and Statements)

宋洲
2023-12-01

Bro脚本语法4-声明和语句(Declarations and Statements)

@(教程)[Bro]

Declarations

NameDescription
module改变当前模块
export从当前模块导出定义
global声明一个全局变量
const声明一个全局常量
type声明一个一用户自定义类型
redef重新定义一个全局值或者扩展一个用户自定义类型
function/event/hook声明一个 function/event/hook

Statements

NameDescription
local声明一个局部变量
add, delete添加或删除元素
print打印到标准输出或文件
for, while, next, break遍历所有容器的元素(for), 条件循环(while).
if表达式bool条件判断执行
switch, break, fallthrough计算表达式和匹配值执行语句
when异步执行
event, schedule调用或调度的事件处理程序
return从 function, hook, 或event 处理程序返回

声明

不能在 function, hook, event 处理函数中使用声明
必须在语句之前使用声明

module
关键字module 用于改变当前的module,这会影响任何随后全局标识符作用范围
例子:

module mymodule

如果一个全局的定义(global)声明在module的后面,那他的作用域在Bro脚本结束的地方或者在下一个module声明的位置,如果一个全局的声明在module的后面,但是在export 语句块中,那么它的作用域将扩展到最后一个load的bro脚本结束,但是在其他的module中它必须通过命名空间操作符(::)来进行引用
在一个bro脚本中,可以有多个module声明,同一个module也可以写在多个不同的bro脚本中

export
export 语句块将当前module中的一个或者多个声明(不能将语句块写在其中)导出,在其他module中,这些全局标识通过操作符”(::)”变得可见
例子:

export {
    redef enum Log::ID += {LOG};

    type Info: record {
        ts: time &log;
        uid: sting &log;
    };

    const conntime = 30sec &redef;

}

注意,在导出块中括号是必须的,而且这个语句块也不需要用分号结束

global
声明一个全局变量
如果在声明的时候不指定类型,那么一定要赋一个初值,bro会实现类型推断,否则就一定要指定类型,或者在类型不能够准确推断的时候,也需要指定类型,例:

global pi = 3.14;
global host: set[addr];
global ciphers:table[sting] of sting = table();

在function ,event ,hook 外面的变量通常都需要用global 关键字(或者使用const 关键字);

在function ,event,hook 处理程序里面则不允许使用这个关键字,另外function 类型(不是函数体)也是可以通过global 关键字声明的.

global 关键字声明的变量作用域从声明的地方开始,到载入的脚本结束(同时,查看module 一节中的描述,当mudule改变的时候,对变量生命周期的改变)

const
const 用于声明一个常量
要求在声明的时候赋初值,同样的,类型可以被推断也可以准确指定

const pi = 3.14;
const ssh_port: port = 22/tcp;

常量的值不可以被改变,唯一的例外是当它是一个全局的常量同时含有 &redef 属性的时候,但是及时这样,他的值只能用 redef 来修改
常量如果定义在function,event, hook中,那么他的作用域就在处理函数内,否则它就是全局的。

注意到const 不能再被local 或者 global 修饰


type
type 关键字用于声明一个用户自定义的类型,新的类型将拥有全局的作用域,也可以用在任何内建类型出现的地方
type 关键字通常被用于定义 record 或者 enum , 当然在处理复杂类型的时候也很有用
例子:

type mytype:table[count] of table[addr,port] of string;
global myvar: mytype;

redef
有三种方式来使用redef : 
用于改变全局变量的值(含有&redef 属性的变量)
用来扩展 record 类型或者 enum 类型
用来明确指定一个新的event处理函数替代之前所有的处理函数

如果你使用 “redef” 来改变一个全局变量(global 或者 const ),那么这个变量需要有&redef 属性,如果要修改的变量是 table,set,或者是pattern,就必须使用 +=操作符来添加新成员,否则直接用=号的话将会分配一个新的值,如果操作的对象是 table 或者set,可以使用-=操作符来移除已有的元素,其他情况下,你要通过=号来重新赋值,例:

redef pi = 3.14;

如果你用redef 来扩展 record 或者 enum,那么你必须使用 +=操作符,对于enum来说,你可以添加更多的枚举常量,对于record ,你可以添加更多的record字段,这时候,通过redef添加的字段,必须含有 &optional 或者 &default 属性,例:

redef enum color += {Blue,Red};
redef record MyRecord += {n2:int &optional;s2:string &optional;};

如果你使用redef 来替换event的处理函数,之前的函数将被完全移除(这个语句后的子event处理函数不会受到影响),语法和常规的event 处理函数的定义完全一样除了前面加了 ”redef“:

redef event myevent(s:string) {print "Redefined",s;}

function/event/hook
参考数据类型一节

语句(Statement)

除了在 function , hook ,event 处理函数中,Statement 只可以出现在所有级联载入(loaded)的Bro 脚本的所有全局声明的后面
所有的Statement必须使用分号结束,单独的语句可以分多行;
Bro 支持的 Statement

add
添加元素到set中

local muset:set[string];
add myset["test"];

break
用于 switch, for, while 语句中

delete
用于从 set 或者 table 中移除元素,或者移除record中具有&optional 属性的字段

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"];

delete myrec$b;

event
用于立即触发一个event处理函数

event myevent("test",5);

fallthrough
用于在swith语句中的case块的尾部,指示执行下一个case 或者 跳到default 标签

for
迭代 string,set,vector,或者table 中的每一个元素并循环执行语句(注意set或者table 中的元素顺序是不确定的),如果元素为空,则不会循环

对于每个迭代对象,如果迭代对象是string,或者是set会分配一个循环变量保存循环中的元素,如果是vector或者是table 的话,将会是一个索引(index).

如果表达式含有多个索引的 table或者是set,,那么循环变量需要是括号内用分号分割的列表

注意 for中的循环变量不能是全局的,而且也不需要在for语句之前提前声明,他的类型会自动推断
同时,也不要在循环语句中添加或删除元素,不然会发生未定义的行为

break 语句 可以立即终止for 循环,next 语句可以用于跳过继续执行

local myset = set(80/tcp,81/tcp);
local mytable = table([10.0.0.1,80/tcp]="s1",[10.0.0.2,81/tcp]="s2"};

for (p in myset)
    print p;
for ([i,j] in mytable) {
    if(mytable[i,j] == "done")
        break;
    if(mytable[i,j] == "skip")
        next;
    print i,j;
}

if
判读一个返回bool的表达式,执行语句

if (x == 2) print "x is 2";

同时,支持 else 语法

if (x == 2)
    print "x is 2";
else 
    print "x is not 2";

local
声明一个本地变量,可以通过初始化值自动推断类型,否则要明确指定类型

local x1 = 5.7;
local x2:double ;
local x3:double = 5.3;

hook ,event,function 处理函数中,要求使用local 关键字来声明变量(除了const 声明和 for 语句中的隐式声明)
local 变量的作用域就仅限于声明的function,event,hook 处理函数中


next
用于 for 和 while 循环中,跳过语句继续执行循环


print
带一个逗号分割的列表表达式,所有表达式将会转换为stirng 并被打印

print 3.14;
print "Results:",x,y;

默认是打印到标准输出,如果第一个表达式是 file 类型,那么就会写到文件中去

如果string 中含有不可打印的字符,那么会被转换成对应的转义字符打印

对于string 的格式化,参考内建函数 fmt (base/bif/bro.bif.bro)


return
用于立即退出function,hook或者event处理函数,在function中如果存在返回值,将明确返回一个表达式或值,在hook和event中不会返回任何值,

function my_func(): string
{
    return "done";
}

event my_event(n:count)
{
    if(n == 0) return;
    print n;
}

return 语句和 when 语句搭配使用,延时return , 当某个条件满足的时候才return ,或者超时return

global X:table[string] of count;

function a() :count
    {
    return when ("a " in X)
        {
            return X["a"];
        }
    timeout 30 sec
        {
        return 0;
        }
    }

event bro_init()
    {
        when ( a() == 42)
            print "expected result";
        print "Waiting for a() to return....";
        X["a"] = 42;
    }

schedule
通过 指定 interval类型的时长延时触发event

schedule 30sec {myevent(x,y,z)};

注意这里大括号是必须的
schedule 表达式会返回一个timer类型的数据,但是一般不用


switch
跳转和匹配语句,匹配不到执行default,没有default 则什么也不执行

所有标签的类型必须和表达式的返回值一致

例(假设get_day_of_week返回一个string)

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;
    }

一个case标签可以匹配多个用逗号分割的表达式
标签必须是常量表达式
每个标签都必须使用 break,fallthrough,return 中的一个结束(return 只能用于function,hook,event中)
大括号是必须的


when
计算一个返回bool的表达式,当返回真或者表达式有效的时候,执行指定的语句

when ( (local x = foo()) && x == 42)
    print x;

如果指定了timeout,那么在指定时间内不能返回T,则执行timeout 语句块

when ( (local x = foo()) && x == 42 )
    print x;
timeout 5sec {
    print "timeout";
}

timeout 语句块中大括号是必须的
when 语句中可以进行变量的声明

when 语句中可以包含异步函数调用比如lookup_hostname(事实上,这也是这种函数唯一可以被调用的地方),也可以包含普通的函数,当一个异步函数被调用,Bro会继续执行when下面的语句块,当函数返回的时候,Bro才会计算when 语句块中的结果,return 章节介绍了如何创建一个异步函数


while
while 条件循环语句
break 语句可以随时终止while循环
next 语句会跳过本次循环继续执行

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
复合语句
通过大括号封装一个或多个语句
大括号语句都需要用分号来结束,但是括号外不需要分号
复合语句通过for,while,if, when 执行多条语句

if ( x == 2 ) {
    print "x is 2";
    ++x;
}

null statement
空语句,只有一个分号的语句

if ( x == 2 )
    ;
 类似资料: