In short: RouterOS scripting is the console’s own language: the same commands you type by hand, lined up and made automatic. Here we see the basics — commands and menus, variables, decisions with :if, loops with :for and :foreach, and selecting objects with [find] — with minimal examples. For practical uses there’s the scripting howto section.
What is scripting on RouterOS?
It isn’t a separate language: it’s the same command line as in WinBox/terminal, automated. When you type /ip address print you’re already “scripting”. A script is a sequence of these commands, plus variables, conditions and loops, which you can save (/system script), run by itself (/system scheduler) or in response to an event (Netwatch, a DHCP lease, and so on).
You try it risk-free in the terminal: one line at a time, and you see the result straight away.
Commands (:) and menus (/): two worlds that live together
Two families of words, with two different prefixes.
- Menus start with
/: they’re the configuration, split into a tree (/ip,/interface,/system…). You act on them withadd,set,remove,print. - Script commands start with
:: they’re the language’s instructions (:local,:if,:for,:foreach,:put,:delay,:log…).
:put prints to the screen, :log info "..." writes to the log. You join strings with a dot:
:put ("CPU at " . [/system resource get cpu-load] . "%")
Variables: :local and :global
A variable holds a value. :local lives only inside its { } block; :global stays in memory and you see it again even in another script.
:local name "ether1"
:global counter 0
:set counter ($counter + 1)
RouterOS recognizes the types by itself: numbers, strings, IP addresses, time (5s, 200ms), booleans, and arrays (lists), which you write in braces: {1;2;3}.
Making decisions: :if
It runs something only if a condition is true. The form is :if (condition) do={ ... } else={ ... }.
:local cpu [/system resource get cpu-load]
:if ($cpu > 80) do={
:log warning ("high CPU: " . $cpu . "%")
} else={
:log info "CPU ok"
}
The comparison operators are =, !=, <, >, <=, >=; the logical ones && (and), || (or), ! (not).
Repeating: :for and :foreach
:for counts from one number to another; :foreach walks the items of a list.
:for i from=1 to=3 do={ :put ("round " . $i) }
:local ports {"ether1";"ether2";"ether3"}
:foreach p in=$ports do={ :put ("configuring " . $p) }
Finding objects: [find]
This is the bit that puzzles most at first. The square brackets [ ] run a command and put its result right where they’re written. Inside, find is a menu command that returns the internal identifiers of the objects that match a condition (written with where). So you don’t need to know the row number: you find it by its property.
# remove the address on ether2, whatever it is
/ip address remove [find interface=ether2]
# disable the firewall rules with a given comment
/ip firewall filter disable [find comment="old rule"]
# the MAC of the interface named ether1
:put [/interface get [find name="ether1"] mac-address]
It’s one of the most-used commands there is, and it shines together with comments. If you tag your configuration lines with a sensible comment, you can then find them by that comment and enable, disable or remove them in bulk: without knowing row numbers, without errors if the order changes or they’re already gone. It’s the clean way to move big chunks of configuration from a script.
# tag a block of rules with the same comment...
/ip firewall filter add chain=forward protocol=tcp dst-port=25 action=drop comment=block-smtp
/ip firewall filter add chain=forward protocol=tcp dst-port=587 action=drop comment=block-smtp
# ...then turn the whole block off or on in one line, by comment
/ip firewall filter disable [find comment=block-smtp]
/ip firewall filter enable [find comment=block-smtp]
# and when it is no longer needed, drop the whole block (no error if empty)
/ip firewall filter remove [find comment=block-smtp]
Hence the advice: use comments not just to remind yourself what a line does, but as functional labels. Smart comments + [find] = dynamic configuration: a script that switches a group of rules on by day and off by night, or rebuilds a whole block from scratch leaving no leftovers. It’s the lever that turns static configuration into something that moves on its own.
Careful not to confuse it with :find (with the colon), which is a different thing: it looks for the position of a piece of text inside a string, or of an element in an array. [find] selects configuration objects; :find works on text.
Waiting and automating
:delay pauses (:delay 2s). To run a script by itself there’s the scheduler:
/system scheduler add name=check interval=5m on-event="/system script run check"
…or you launch it in response to an event, for example when Netwatch sees a link go down.
An example that puts it all together
:foreach r in=[/ip firewall filter find where chain=input] do={
:local pkt [/ip firewall filter get $r packets]
:if ($pkt > 1000000) do={
:log info ("heavily used rule: " . [/ip firewall filter get $r comment] . " = " . $pkt)
}
}
It walks the input rules (:foreach + [find where]), reads a value (get), decides (:if) and writes to the log. These are the basics of everything.
What now?
These are the foundations. For the practical “nuggets” — Telegram alerts, scripts on DHCP leases, Netwatch, and even making the router play a tune — go to the Scripting and automation section of the Dojo howtos.
Syntax verified against the RouterOS 7.x documentation (console and developer guide): :local/:global, :if … do= … else=, :for from= to= do=, :foreach … in= do=, the menu command find … where with the substitution brackets [ ], and :find as a search over strings/arrays.
Frequently asked questions
What’s the difference between / and :?
The / opens a configuration menu (/ip, /interface); the : are the scripting language’s commands (:if, :for, :put). The former change the configuration, the latter drive the script.
What do the square brackets [ ] do?
They run a command and insert its result in their place. [/system resource get cpu-load] becomes the CPU number; [find …] becomes the list of objects found.
Are [find] and :find the same thing?
No. [find] is a menu command that returns the ids of configuration objects (with where); :find looks for the position of text in a string or an array.
Where do I test scripts without breaking anything?
In the terminal, one line at a time: read-only commands (print, get, :put) change nothing. For serious tests, a lab router or a CHR.



