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 with add, 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.