本手册介绍了 RouterOS 内置的强大脚本语言。
脚本主机提供了一种方法,通过执行绑定到某些事件发生的用户定义脚本来自动执行某些路由器维护任务。
脚本可以存储在脚本存储库中,也可以直接写入控制台。用于触发脚本执行的事件包括但不限于系统计划程序、流量监视工具和 Netwatch 工具生成的事件。
如果您已经熟悉RouterOS中的脚本,则可能希望查看我们的提示和技巧。
生产线结构
RouterOS脚本分为许多命令行。命令行将逐个执行,直到脚本结束或发生运行时错误。
命令行
RouterOS 控制台使用以下命令语法:
[prefix] [path] command [uparam] [param=[value]] .. [param=[value]]
- [前缀] - “:”或“/”字符,指示命令是 ICE 还是路径。可能是必需的,也可能不是必需的。
- [路径] - 所需菜单级别的相对路径。可能是必需的,也可能不是必需的。
- 命令 - 指定菜单级别可用的命令之一。
- [uparam] - 未命名参数,如果命令需要,则必须指定它。
- [参数] - 命名参数序列,后跟相应的值
命令行的末尾由标记“;”或换行符表示。有时,“;”或换行符不需要结束命令行。
内部的单个命令不需要任何结尾的命令字符。命令的结束由整个脚本的内容决定(), [] or {}
:if ( true ) do={ :put "lala" }
另一个命令行中的每个命令行都以方括号“[ ]”(命令串联)开头和结尾。
:put [/ip route get [find gateway=1.1.1.1]];
请注意,上面的代码包含三个命令行:
- :p ut
- /ip route get
- 查找网关 = 1.1.1.1
可以通过遵循行连接规则从多个物理行构造命令行。
物理线路
物理行是由行尾 (EOL) 序列终止的字符序列。可以使用任何标准平台线端接序列:
- unix – ASCII LF;
- windows – ASCII CR LF;
- mac – ASCII CR;
可以使用新行字符的标准 C 约定(\n 字符)。
评论
注释以哈希字符 (#) 开头,在物理行的末尾结束。哈希符号之前不允许使用空格或任何其他符号。语法会忽略注释。如果 (#) 字符出现在字符串中,则不将其视为注释。
例
# this is a comment
# bad comment
:global a; # bad comment
:global myStr "lala # this is not a comment"
线连接
可以使用反斜杠字符 (\) 将两个或多个物理行连接成逻辑行。以反斜杠结尾的行不能带有注释。反斜杠不会继续注释。反斜杠不会继续标记,但字符串文本除外。反斜杠在字符串文本外的行上的其他位置是非法的。
例
:if ($a = true \
and $b=false) do={ :put “$a $b”; }
:if ($a = true \ # bad comment
and $b=false) do={ :put “$a $b”; }
# comment \
continued – invalid (syntax error)
令牌之间的空格
空格可用于分隔标记。仅当两个令牌的串联可以解释为不同的令牌时,才需要空格。例:
{
:local a true; :local b false;
# whitespace is not required
:put (a&&b);
# whitespace is required
:put (a and b);
}
不允许使用空格字符
- 在'<参数>='之间
- 在 'from=' 'to=' 'step=' 'in=' 'do=' 'else=' 之间
例:
#incorrect:
:for i from = 1 to = 2 do = { :put $i }
#correct syntax:
:for i from=1 to=2 do={ :put $i }
:for i from= 1 to= 2 do={ :put $i }
#incorrect
/ip route add gateway = 3.3.3.3
#correct
/ip route add gateway=3.3.3.3
范围
变量只能在脚本的某些区域中使用。这些区域称为作用域。作用域确定变量的可见性。有两种类型的作用域 - 和 。在块中声明的变量只能在该块和由该块包围的块内访问,并且只能在声明点之后访问。globallocal
全球范围
全局作用域或根作用域是脚本的默认作用域。它是自动创建的,无法关闭。
本地范围
用户可以定义自己的组来阻止对某些变量的访问,这些作用域称为局部作用域。每个局部作用域都括在大括号 (“{ }”) 中。
{
:local a 3;
{
:local b 4;
:put ($a+$b);
}
#line below will show variable b in light red color since it is not defined in scope
:put ($a+$b);
}
在上面的代码中,变量具有局部作用域,并且在闭合大括号后将无法访问。b
注意:写入终端的每一行都被视为本地范围
例如,定义的局部变量在下一个命令行中将不可见,并且将生成语法错误
[admin@MikroTik] > :local myVar a;
[admin@MikroTik] > :put $myVar
syntax error (line 1 column 7)
警告:****不要在局部作用域内定义全局变量。
请注意,即使变量也可以定义为全局变量,除非尚未定义,否则它只能从其作用域中可用。
{
:local a 3;
{
:global b 4;
}
:put ($a+$b);
}
上面的代码将生成错误。
关键字
以下单词是关键字,不能用作变量和函数名称:
and or in
分隔符
以下标记用作语法中的分隔符:
() [] {} : ; $ /
数据类型
RouterOS 脚本语言具有以下数据类型:
类型 | 描述 |
---|---|
num (number) | - 64位有符号整数,可能的十六进制输入; |
bool (boolean) | - 值可以是蜜蜂或true``false ; |
str (string) | - 字符序列; |
ip | - IP地址; |
ip-prefix | - IP 前缀; |
ip6 | - IPv6 地址 |
ip6-prefix | - IPv6 前缀 |
id (internal ID) | - 以“*”符号为前缀的十六进制值。每个菜单项都分配有唯一的编号 - 内部ID; |
time | - 日期和时间值; |
array | - 在数组中组织的值序列; |
nil | - 默认变量类型,如果未分配值; |
常量转义序列
以下转义序列可用于定义字符串中的某些特殊字符:
" | 插入双引号 |
---|---|
\ | 插入反斜杠 |
\n | 插入换行符 |
\r | 插入回车符 |
\t | 插入水平选项卡 |
$ | 输出 用于链接变量。 |
? | 输出?字符。否则?用于在控制台中打印“帮助”。 |
_ | - 空间 |
\a | - 贝尔 (0x07) |
\b | - 退格键(0x08) |
\f | - 换行(0xFF) |
\v | 插入垂直选项卡 |
\xx | 从十六进制值打印字符。十六进制数字应使用大写字母。 |
例
:put "\48\45\4C\4C\4F\r\nThis\r\nis\r\na\r\ntest";
将显示在显示屏上
HELLOThisisatest
运营商
算术运算符
RouterOS 脚本语言中支持常用的算术运算符
算子 | 描述 | 例 |
---|---|---|
"+" | 二进制加法 | :put (3+4); |
"-" | 二进制减法 | :put (1-6); |
"*" | 二进制乘法 | :put (4*5); |
"/" | 二进制除法 | :put (10 / 2); :put ((10)/2) |
"%" | 模运算 | :put (5 % 3); |
"-" | 一元否定 | { :local a 1; :put (-a); } |
注意:要进行划分,您必须在红利周围使用大括号或空格,以免被误认为是IP地址
关系运算符
算子 | 描述 | 例 |
---|---|---|
"<" | 少 | :put (3<4); |
">" | 大 | :put (3>4); |
"=" | 平等 | :put (2=2); |
"<=" | 小于或等于 | |
">=" | 大于或等于 | |
"!=" | 不等于 |
逻辑运算符
算子 | 描述 | 例 | ||
---|---|---|---|---|
“!” | 逻辑 NOT | :put (!true); |
||
“&&” , “and” | 逻辑和 | :put (true&&true) |
||
“||” , “or” | 逻辑或 | `:put (true | false);` | |
“in” | :put (1.1.1.1/32 in 1.0.0.0/8); |
按位运算符
按位运算符正在处理数字,IP和IPv6地址数据类型。
算子 | 描述 | 例 | ||
---|---|---|---|---|
“~” | 位反转 |
:put (~0.0.0.0) :put (~::ffff)
|
||
“|” | 按位 OR。对每对对应位执行逻辑 OR 操作。在每对中,如果其中一个位或两个位均为“1”,则结果为“1”,否则结果为“0”。 | `:put (192.168.88.0 | 0.0.0.255) :put (2001::1 |
::ffff)` |
“^” | 按位异或。与 OR 相同,但如果两个位不相等,则每个位置的结果是“1”,如果位相等,则为“0”。 |
:put (1.1.1.1^255.255.0.0) :put (2001::ffff:1^::ffff:0)
|
||
“&” | 按位和。在每对中,如果第一位和第二位是“1”,则结果为“1”。否则,结果为“0”。 |
:put (192.168.88.77&255.255.255.0) :put (2001::1111&ffff::)
|
||
“<<” | 按给定位数左移,IPv6 地址数据类型不支持 | :put (192.168.88.77<<8) |
||
“>>” | 按给定位数右移,IPv6 地址数据类型不支持 | :put (192.168.88.77>>24) |
使用“&”运算符计算给定IP和CIDR网络掩码的子网地址:
{
:local IP 192.168.88.77;
:local CIDRnetmask 255.255.255.0;
:put ($IP&$CIDRnetmask);
}
从给定的 IP 地址获取最后 8 位:
:put (192.168.88.77&0.0.0.255);
使用“|”运算符和倒置 CIDR 掩码计算广播地址:
{
:local IP 192.168.88.77;
:local Network 192.168.88.0;
:local CIDRnetmask 255.255.255.0;
:local InvertedCIDR (~$CIDRnetmask);
:put ($Network|$InvertedCIDR)
}
串联运算符
算子 | 描述 | 例 |
---|---|---|
“.” | 连接两个字符串 | :put (“concatenate” . “ “ . “string”); |
“,” | 连接两个数组或将元素添加到数组 | :put ({1;2;3} , 5 ); |
可以在没有串联运算符的情况下向字符串添加变量值:
:global myVar "world";
:put ("Hello " . $myVar);
# next line does the same as above
:put "Hello $myVar";
通过在字符串中使用 () 可以在字符串中添加表达式:
:local a 5;
:local b 6;
:put " 5x6 = $($a * $b)";
:put " We have $[ :len [/ip route find] ] routes";
其他运营商
算子 | 描述 | 例 |
---|---|---|
“[]” | 命令替换。只能包含单个命令行 | :put [ :len "my test string"; ]; |
“()” | 子表达式或分组运算符 | :put ( "value is " . (4+5)); |
“$” | 替换运算符 | :global a 5; :put $a; |
“~” | 将值与 POSIX 扩展正则表达式匹配的二进制运算符 | 打印网关以 202 结尾的所有路由 /ip route print where gateway~"^[0-9 \\.]*202\$"
|
“->” | 按键获取数组元素 | [admin@x86] >:global aaa {a=1;b=2} [admin@x86] > :put ($aaa->"a") 1 [admin@x86] > :put ($aaa->"b") 2 |
变量
脚本语言有两种类型的变量:
注意:从 v6.2 开始,可以有未定义的变量。当变量未定义时,解析器将尝试查找设置的变量,例如,通过DHCP或热点lease-script on-login
注意:可变值大小限制为 4096 字节
除内置的 RouterOS 变量外,每个变量都必须在使用本地或全局关键字之前声明。未定义的变量将被标记为未定义,并将导致编译错误。例:
# following code will result in compilation error, because myVar is used without declaration
:set myVar "my value";
:put $myVar
正确代码:
:local myVar;
:set myVar "my value";
:put $myVar;
例外情况是使用设置的变量,例如,通过 DHCP 设置lease-script
/system script
add name=myLeaseScript policy=\
ftp,reboot,read,write,policy,test,winbox,password,sniff,sensitive,api \
source=":log info \$leaseActIP\r\
\n:log info \$leaseActMAC\r\
\n:log info \$leaseServerName\r\
\n:log info \$leaseBound"
/ip dhcp-server set myServer lease-script=myLeaseScript
变量名称中的有效字符是字母和数字。如果变量名包含任何其他字符,则应将变量名放在双引号中。例:
#valid variable name
:local myVar;
#invalid variable name
:local my-var;
#valid because double quoted
:global "my-var";
如果变量最初定义时不带值,则变量数据类型设置为 nil,否则数据类型由脚本引擎自动确定。有时需要从一种数据类型转换为另一种数据类型。它可以使用数据转换命令来实现。例:
#convert string to array
:local myStr "1,2,3,4,5";
:put [:typeof $myStr];
:local myArr [:toarray $myStr];
:put [:typeof $myArr]
变量名称区分大小写。
:local myVar "hello"
# following line will generate error, because variable myVAr is not defined
:put $myVAr
# correct code
:put $myVar
不带值的 Set 命令将取消定义变量(从环境中删除,v6.2 中新增)
#remove variable from environment
:global myVar "myValue"
:set myVar;
保留的变量名称
所有内置的 RouterOS 属性都是保留变量。与 RouterOS 内置属性定义相同的变量可能会导致错误。为避免此类错误,请使用自定义名称。
例如,以下脚本将不起作用:
{
:local type "ether1";
/interface print where name=$type;
}
但将使用不同的定义变量:
{
:local customname "ether1";
/interface print where name=$customname;
}
命令
全局命令
每个全局命令都应以 “:” 标记开头,否则它将被视为变量。
命令 | 语法 | 描述 | 例 |
---|---|---|---|
/ | 转到根菜单 | ||
.. | 按一个菜单级别返回 | ||
? | 列出所有可用的菜单命令和简要说明 | ||
global | :global <var> [<value>] |
定义全局变量 | :global myVar "something"; :put $myVar; |
local | :local <var> [<value>] |
定义局部变量 | { :local myLocalVar "I am local"; :put $myVar; } |
beep | :beep <freq> <length> |
内置扬声器蜂鸣音 | `` |
delay | :delay <time> |
在给定时间段内不执行任何操作 | `` |
put | :put <expression> |
将提供的参数放到控制台 | `` |
len | :len <expression> |
返回字符串长度或数组元素计数 | :put [:len "length=8"]; |
typeof | :typeof <var> |
返回变量的数据类型 | :put [:typeof 4]; |
pick | :pick <var> <start>[<end>] |
元素或子字符串的返回范围。如果未指定结束位置,则 将仅返回数组中的一个元素。 | :put [:pick "abcde" 1 3] |
log | :log <topic> <message> |
将消息写入系统日志。可用主题包括"debug, error, info and warning"
|
:log info "Hello from script"; |
time | :time <expression> |
返回执行命令所需的时间间隔 | :put [:time {:for i from=1 to=10 do={ :delay 100ms }}]; |
set | :set <var> [<value>] |
为声明的变量赋值。 | :global a; :set a true; |
find | :find <arg> <arg> <start> |
返回子字符串或数组元素的位置 | :put [:find "abc" "a" -1]; |
environment | :environment print <start> |
打印初始化的变量信息 | :global myVar true; :environment print; |
terminal | `` | 终端相关命令 | `` |
error | :error <output> |
生成控制台错误并停止执行脚本 | `` |
execute | :execute <expression> |
在后台执行脚本。结果可以通过设置参数写入文件。file | { :local j [:execute {/interface print follow where [:log info ~Sname~]}]; :delay 10s; :do { /system script job remove $j } on-error={} } |
parse | :parse <expression> |
解析字符串并返回解析的控制台命令。可用作功能。 | :global myFunc [:parse ":put hello!"];$myFunc; |
resolve | :resolve <arg> |
返回给定 DNS 名称的 IP 地址 | :put [:resolve "www.mikrotik.com"]; |
toarray | :toarray <var> |
将变量转换为数组 | `` |
tobool | :tobool <var> |
将变量转换为布尔值 | `` |
toid | :toid <var> |
将变量转换为内部 ID | `` |
toip | :toip <var> |
将变量转换为 IP 地址 | `` |
toip6 | :toip6 <var> |
将变量转换为 IPv6 地址 | `` |
tonum | :tonum <var> |
将变量转换为整数 | `` |
tostr | :tostr <var> |
将变量转换为字符串 | `` |
totime | :totime <var> |
将变量转换为时间 | `` |
菜单特定命令
常用命令
大多数子菜单中都提供以下命令:
命令 | 语法 | 描述 |
---|---|---|
add | add <param>=<value>..<param>=<value> |
添加新项目 |
remove | remove <id> |
删除所选项目 |
enable | enable <id> |
启用所选项目 |
disable | disable <id> |
禁用所选项目 |
set | set <id> <param>=<value>..<param>=<value> |
更改所选项目参数,一次可以指定多个参数。可以通过在参数前指定“!”来取消设置参数。 例: `/ip firewall filter add chain=blah action=accept protocol=tcp port=123 nth=4,2printset 0 !port chain=blah2 !nth protocol=udp``` |
get | get <id> <param>=<value> |
获取所选项目参数值 |
print <param><param>=[<value>] |
打印菜单项。输出取决于指定的打印参数。此处介绍了最常见的打印参数 | |
export | export [file=<value>] |
从当前菜单及其子菜单(如果存在)导出配置。如果指定了 file 参数,则输出将写入扩展名为“.rsc”的文件,否则输出将打印到控制台。导出的命令可以通过导入命令导入 |
edit | edit <id> <param> |
在内置文本编辑器中编辑所选项目属性 |
find | find <expression> |
返回与给定表达式匹配的项的内部编号列表。例如::put [/interface find name~"ether"]
|
进口
导入命令可从根菜单获得,用于从导出命令创建或手动编写的文件中导入配置。
打印参数
有几个参数可用于打印命令:
参数 | 描述 | 例 |
---|---|---|
append | `` | |
as-value | 将输出打印为参数数组及其值 | :put [/ip address print as-value] |
brief | 打印简要说明 | `` |
detail | 打印详细说明,输出不如简要输出可读,但可用于查看所有参数 | `` |
count-only | 仅打印菜单项计数 | `` |
file | 将输出打印到文件 | `` |
follow | 打印所有当前条目并跟踪新条目,直到按下ctrl-c,在查看日志条目时非常有用 | /log print follow |
follow-only | 在按下 ctrl-c 之前仅打印和跟踪新条目,这在查看日志条目时非常有用 | /log print follow-only |
from | 仅从指定项目打印参数 | /user print from=admin |
interval | 以选定的时间间隔连续打印输出,有助于跟踪不可接受的更改follow
|
/interface print interval=2 |
terse | 以紧凑和机器友好的格式显示细节 | `` |
value-list | 每行显示一个值(适合解析目的) | `` |
without-paging | 如果输出不适合控制台屏幕,则不要停止,一次性打印所有信息 | `` |
where | 表达式后跟可用于筛选出匹配条目的参数 | /ip route print where interface="ether1" |
一次可以指定多个参数,例如,/ip route print count-only interval=1 where interface="ether1"
循环和条件语句
循环
命令 | 语法 | 描述 |
---|---|---|
do..while | :do { <commands> } while=( <conditions> ); :while ( <conditions> ) do={ <commands> }; |
执行命令,直到满足给定条件。 |
for | :for <var> from=<int> to=<int> step=<int> do={ <commands> } |
在给定的迭代次数内执行命令 |
foreach | :foreach <var> in=<array> do={ <commands> }; |
为列表中的每个元素执行命令 |
条件语句
命令 | 语法 | 描述 |
---|---|---|
if | :if(<condition>) do={<commands>} else={<commands>} <expression> |
如果给定条件是然后执行块中的命令,否则在块中执行命令(如果指定)。true``do``else
|
例:
{
:local myBool true;
:if ($myBool = false) do={ :put "value is false" } else={ :put "value is true" }
}
功能
脚本语言不允许直接创建函数,但是您可以使用:p arse命令作为解决方法。
从 v6.2 开始,添加了新的语法,以便更轻松地定义此类函数,甚至传递参数。也可以使用 :return 命令返回函数值。
请参阅以下示例:
#define function and run it
:global myFunc do={:put "hello from function"}
$myFunc
output:
hello from function
#pass arguments to the function
:global myFunc do={:put "arg a=$a"; :put "arg '1'=$1"}
$myFunc a="this is arg a value" "this is arg1 value"
output:
arg a=this is arg a value
arg '1'=this is arg1 value
请注意,有两种方法可以传递参数:
- 传递具有特定名称的 arg(在我们的示例中为“a”)
- 传递值而不带 arg 名称,在这种情况下,arg “1”, “2” ..使用“n”。
返回示例
:global myFunc do={ :return ($a + $b)}
:put [$myFunc a=6 b=2]
output:
8
您甚至可以从脚本环境中克隆现有脚本并将其用作函数。
#add script
/system script add name=myScript source=":put \"Hello $myVar !\""
:global myFunc [:parse [/system script get myScript source]]
$myFunc myVar=world
output:
Hello world !
警告:如果函数包含已定义的全局变量,其名称与传递的参数的名称匹配,则全局定义的变量将被忽略,以便与为旧版本编写的脚本兼容。此功能在将来的版本中可能会更改。避免使用与全局变量同名的参数。
例如:
:global my2 "123"
:global myFunc do={ :global my2; :put $my2; :set my2 "lala"; :put $my2 }
$myFunc my2=1234
:put "global value $my2"
输出将为:
1234
lala
global value 123
嵌套函数示例
注意:要调用另一个函数,需要声明其名称(与变量相同)
:global funcA do={ :return 5 }
:global funcB do={
:global funcA;
:return ([$funcA] + 4)
}
:put [$funcB]
Output:
9
捕获运行时错误
从 v6.2 开始,脚本能够捕获运行时错误。
例如,[code]:reslove[/code] 命令如果失败将引发错误并中断脚本。
[admin@MikroTik] > { :put [:resolve www.example.com]; :put "lala";}
failure: dns name does not exist
现在我们要捕获此错误并继续使用我们的脚本:
:do {
:put [:resolve www.example.com];
} on-error={ :put "resolver failed"};
:put "lala"
output:
resolver failed
lala
阵列操作
警告:数组中的键名包含除小写字符以外的任何字符,应将其放在引号中
例如:
[admin@ce0] > {:local a { "aX"=1 ; ay=2 }; :put ($a->"aX")}
1
遍历键和值
[admin@ce0] > :foreach k,v in={2; "aX"=1 ; y=2; 5} do={:put ("$k=$v")}
0=2
1=5
aX=1
y=2
[admin@ce0] > :foreach k in={2; "aX"=1 ; y=2; 5} do={:put ("$k")}
2
5
1
2
注意:如果数组元素具有键,则这些元素按字母顺序排序,不带键的元素在具有键的元素之前移动,并且其顺序不会更改(请参阅上面的示例)。
更改单个数组元素的值
[admin@MikroTik] > :global a {x=1; y=2}
[admin@MikroTik] > :set ($a->"x") 5
[admin@MikroTik] > :environment print
a={x=5; y=2}
脚本存储库
子菜单级别: /system script
包含所有用户创建的脚本。可以通过几种不同的方式执行脚本:
注意:只有具有相同或更高权限的脚本(包括调度程序、netwatch 等)才能执行其他脚本。
属性 | 描述 |
---|---|
comment (字符串;违约:) | 脚本的描述性注释 |
dont-require-permissions (是 | 否;默认值:否) | 在执行脚本时绕过权限检查,当从具有有限权限的服务(如 Netwatch)执行脚本时很有用 |
name (字符串;默认值:“脚本[编号]”) | 脚本的名称 |
policy (字符串;违约:) | 适用政策列表: ftp - 可以通过ftp远程登录,并从路由器发送和检索文件密码 - 更改密码策略 - 管理用户策略,添加和删除用户读取 - 可以检索配置重新启动 - 可以重新启动路由器敏感 - 允许更改“隐藏敏感”参数嗅探 - 可以运行嗅探器,火炬等测试 - 可以运行 ping、跟踪路由、带宽测试写入 - 可以更改配置在此处阅读更详细的政策说明 |
source (字符串;) | 脚本源代码 |
只读状态属性:
属性 | 描述 |
---|---|
last-started (日期) | 上次调用脚本的日期和时间。 |
owner (字符串) | 创建脚本的用户 |
run-count (整数) | 计算脚本执行次数的计数器 |
菜单特定命令
命令 | 描述 |
---|---|
run (运行 [id|name]) | 按 ID 或名称执行指定的脚本 |
环境
子菜单级别:
/system script environment
/environment
包含所有用户定义的变量及其分配的值。
[admin@MikroTik] > :global example;
[admin@MikroTik] > :set example 123
[admin@MikroTik] > /environment print
"example"=123
只读状态属性:
属性 | 描述 |
---|---|
name (字符串) | 变量名 |
user (字符串) | 定义变量的用户 |
value () | 分配给变量的值 |
工作
子菜单级别: /system script job
包含所有当前正在运行的脚本的列表。
只读状态属性:
财产 | 描述 |
---|---|
owner (字符串) | 正在运行脚本的用户 |
policy (数组) | 应用于脚本的所有策略的列表 |
started (日期) | 脚本启动的本地日期和时间 |
网友评论