Notes on Terminal and scripting commands in the Mikrotik router scripting language Denis de Castro April 21 Last updates, 17 Jul 21, 14 Mar 22, 12 Apr 22, 20 Jun 22 file, Terminal and scripting commands v10.txt Corrections please to BMCP@protonmail.com ________________________________________________________________________________________________ Contents General case-sensitivity semicolons variables working with files logic: do statement if statement for command foreach statement dates export config. files functions return example get and set testing length or existence Terminal use essentials scope of commands showing scripts running scripts Scripts importing and exporting permissions comments get command set command find command pick command delay in script exit from script logging email string handling tricks datetime function random numbers wait and show elapsed time get interface list members Bibliography ________________________________________________________________________________________________ General aspects of the router scripting language which apply both in Terminal and in Scripts - - - - - - - - - Case-sensitivity Commands and their parameters are case-sensitive - - - - - - - - - Semicolon You do not need semicolon at the end of each line, only when multiple commands on same line (Jotne), or for subsequent comment - - - - - - - - - Variables Names of variables can only contain digits and letters, eg myVar Declared with :local myVar "something" for use only in that script or :global myVar "something" for use in all scripts, but must be first declared in all that use it Can be declared empty ("") Declare variables in the global scope; ie not within curly brackets! Global variables are tracked and displayed at System>Scripts>Environment - - - - - - - - - Commands for working with files, using variables in place of static content or file names: (from https://mikrotik-routeros.com/2016/06/the-basics-of-reading-and-writing-files-in-routeros/) 1. To create a new file /file print file=$filename 2. To read an existing file :set $filedata [/file get $filename contents] 3. To write to an existing file /file set $filename contents=$newdata 4. To append to an existing file file set $filename contents=([get $filename contents] . $newdata) - - - - - - - - - do statement in one line :do {:local value "this"} use of "on-error" e.g. :do { :put [:resolve www.%%%%%.com]; } on-error={ :put "resolver failed"}; :put "lala" [https://wiki.mikrotik.com/wiki/Manual:Scripting#Catch_run-time_errors] But 'on-error' seems not to work after :return xxx or after or before an } else={. - - - - - - - - - If statement # Mikrotik "if" statement example :global version [/system resource get version ] :if ($version>=6.5) do={ # Do some stuff } else={ # Do some other stuff } Note the syntax '} else={', which must be just so. - - - - - - - - - For command eg :for i from=0 to=12 do={/interface bridge port set $i pvid=101} - - - - - - - - - Foreach statement 'perform an action against each matching instance' (Andrew Cox) :foreach itemVar in= do={ # one or more commands that do something with $itemVar } (The can be one of a few things, including a variable which contains an array, an array literal like {1;2;3}, or as in this case, a command inside square brackets which returns an array. An array in 'tik scripting is a bit like a JavaScript Array (and different than pretty much every other language)—it can act as a list of items in some cases, and a dictionary/hashtable in others. - from https://www.reddit.com/r/mikrotik/comments/ghmuny/export_certain_fields_only/) examples :foreach in={/ip arp find} do={$cmd} :foreach line in=[/interface ethernet find] do={:put [/interface ethernet get $line default-name]} - - - - - - - - - Dates snippet to get dates into the format year-month-day (source, www.cloutik.com/time-scripting) :local date [/system clock get date]; :local months {“jan”=”01″;”feb”=”02″;”mar”=”03″;”apr”=”04″;”may”=”05″;”jun”=”06″;”jul”=”07″;”aug”=”08″;”sep”=”09”;”oct”=10;”nov”=11;”dec”=12}; :local day [:tonum [:pick $date 4 6]];:local year [:tonum [:pick $date 7 11]];:local month [:pick $date 0 3];:local mm (:$months->$month); :local newdate “$year-$mm-$day”; - - - - - - - - - Functions #define function and run it :global myFunc do={:put "hello from function"} Call a function thus: $myFunc output: hello from function [source, https://forum.mikrotik.com/viewtopic.php?t=75081 and see particularly https://forum.mikrotik.com/viewtopic.php?t=75081#p477543] So a function can be defined say at the beginning of a script (eg as a local variable) and called from further on. It is possible to keep a function available in its own script, to be called on by other scripts when they need it. The function script needs • to have been run before it can be called (eg by having it run at startup by the scheduler) • to have the function it serves be defined as 'global' • have its name has to be declared in the script that uses it (eg with :global ). -------- Return example :global myFunc do={ :return ($a + $b)} :put [$myFunc a=6 b=2] output: 8 -------- Pass arguments to a funtion Can name arguments like this: :put [$myFunc a=6 b=2] and in the function use $a, $b etc Otherwise values can be passed in sequence and are used in the function in order, e.g. $myFunc "this is the value of argument 1" "this is the value of argument 2" and in the function use $1, $2 etc - - - - - - - - - Get and Set get example (uses 'item number'. This number is established by the print command and could change) :local ipv6WAN [/ipv6 dhcp-client get 0 interface]; set example (uses list of numbers) interface set 0,1,2 mtu=1460 - - - - - - - - - Testing length or existence (see also 'find command' below) Test if file exists If you need to create a file on a Mikrotik router, but you first want to test if there is a file with that name, here is a little line you can use in a script to check for it. :if ([:len [/file find name=testfile.txt]] > 0) do={} \ else={/file print file=testfile; :delay 1; /file […] Test whether a character is present in a string, eg is x present in abcd? :if ([:len [:find "abcd" "x"]] > 0) do={:put "Found";} else={:put "Not Found";}; [source, https://forum.mikrotik.com/viewtopic.php?t=95921] Test whether an entry exists by looking for a nonzero length either for the number of entries or a value within one, -- tests if a line exists with a certain value in a field: :if ([:len [/ipv6 dhcp-client find where interface=$WANinterface]] >0 ) do={ ... } -- tests if a line exists with a certain value in a field and another value having a nonzero length: :if ([:len [ip dhcp-server lease get [find where mac-address=$currentMAConBridge] address ]] > 0 ) do={ .... } - - - - - - - - - ________________________________________________________________________________________________ Terminal - - - - - - - - - Essentials of Terminal Use https://help.mikrotik.com/docs/display/ROS/Command+Line+Interface - - - - - - - - - Command scope in the Terminal In the Terminal set a value thus :global hello "hi" (variable can be set empty with a blank; setting it to "" probably sets as a string) (or with 'local', but that variable's scope restricted to that line in the T.) Echo it thus :put $hello - - - - - - - - - Display a page of data from Webfig in the terminal Use 'print value-list', eg /interface ethernet print value-list - - - - - - - - - Showing scripts in Terminal /system script print For a particular script, /system script print from=name_of_script Sometimes reddish highlighting will pinpoint a fault Running scripts in the terminal Each line written in terminal is treated as local scope But a script can be run enclosed in '{}' See https://forum.mikrotik.com/viewtopic.php?t=107970 (not much use currently as cannot 'paste' into Terminal) Run a script in Terminal with error reporting: /system script run myScriptName [source, https://wiert.me/2017/06/08/mikrotik-scripting-language-a-list-of-questions-i-had-linking-to-the-forum-messages-having-answers/. Original source, https://forum.mikrotik.com/viewtopic.php?t=3294] And try this (built in code editor) /system script edit source "F5 to refresh highlighting", did not work in macOS. ________________________________________________________________________________________________ Scripts [refs. 1, 2] - - - - - - - - - To export a file containing the current scripts, in the Terminal; /system script export file=current_scripts (do not include '.rsc' suffix) To import these scripts, using Webfig, in Files, upload the .rsc file then in Terminal; /import current_scripts.rsc (or other script-containing .rsc file) Scripts are then available in System>Scripts Similarly with /system scheduler or any other part of the configuration. To export the whole configuration /export file=“filename” And import with /import filename.rsc This can be done to restore the router configuration after a reset with /system reset-configuration. It clears all configuration of the router and sets it to the default including the login name and password ('admin' and no password) - - - - - - - - - Suggested workflow for developing scripts Draft the script in System>Scripts>New and apply Change to the Terminal (maybe in another browser tab) Print the script (/system script print from=script-name), looking for clues to errors (dark brown highlighting...) Optionally, edit the script (/system script edit script-name source). More errors may show up (scripts prints at top of window: Control-C to return to command line etc). Run the script in the Terminal (/system script run script-name) and see what happens. Can use 'put' command to trace execution eg :put "got to here" # for debugging, remove this line. - - - - - - - - - Permissions Unless script in System>Script has 'do not require permissions' ticked, whatever or whoever fires the script needs all the permissions in the 'policy' list of the script. So generally for scripts to be fired from the scheduler, set the schedular entry to have all permissions (except 'dude', which does not seem to be available in script policies). - - - - - - - - - Comments Start a comment line with a hash A comment can be placed after the end of a command line, if the line terminates with a semi-colon (and sometimes in other circumstances). - - - - - - - - - Get command example :put [/system routerboard get serial-number] - - - - - - - - - Set command Examples (from ref. 5). # Set custom MAC on the first bridge (numbers=0) /interface bridge set admin-mac=6C:3B:6B:18:B5:ED auto-mac=no numbers=0 # Set custom MAC on a specific bridge named bridge3 /interface bridge set admin-mac=6C:3B:6B:18:B5:ED auto-mac=no [ find name=bridge3 ] # Set custom MAC on the first ethernet port, no matter what it is currently named /interface ethernet set mac-address=6C:3B:6B:05:B1:A9 [ find default-name=ether1 ] - - - - - - - - - Find command the find command uses 'internal ID numbers', see ref. 3. Find length or existence of items example 1. :if ([:len [/ip neighbor find]] > 0) do={:put "Found";} else={:put "Not Found";}; example 2. :local countcriticals [:len [/log find where topics~"critical"]]; if ( $countcriticals > 0) do={ ... } Get one line of data from a webfig page :put [/ip route get [find gateway=10.16.16.1]] Get an item from one line where another item on that line is known example 1. :global gwAddress [/ip address get [find interface="ether1"] address] example 2. :put [ip dhcp-server lease get [find host-name=my-HP] address] example 3. :local thishostport [/interface ethernet switch host get [find where mac-address=$thishostmacaddress] ports]; example 4. :foreach line in=[/interface bridge host find where on-interface=$ifName] do={ :set $currentMAConBridge [/interface bridge host get $line mac-address ];} Get data from several lines of a webfig page example 1. :foreach line in=[/ip address find] do={ :put [/ip address get $line network]} ('find' here returns a list of internal numbers for items that are matched by the expression) and for inclusion in a script, eg :foreach line in=[/ip address find] do={ :set theReport ($theReport . [/ip address get $line network] ) } example 2. /ip dhcp-server lease :foreach i in=[print as-value] do={:put "$($i->"comment") $($i->"address") $($i->"host-name")"} - - - - - - - - - Pick command :pick [] eg :put [:pick "abcde" 1 3] Result is bc. the 'start' index is zero, and the 'end' is beyond the last character of the variable! to get part of an item from a composite item on a line, then trim it: :set $thisIPAddr [/ip address get $line address]; :set $shortAddr [:pick $thisIPAddr 0 [:find $thisIPAddr "/"]]; Using find for part of a variable name; eg {:put [:find "abcde.backup" ".backup"]} - - - - - - - - - Delay in script eg :delay 4000ms :delay 10s - - - - - - - - - Exit to exit a script: :error "bye!" - - - - - - - - - Logging to make entry in the log :log info “this text” (or can use entries other than ‘info’) - - - - - - - - - Email Attach more than one file to an email :local files {$backupconf;$backuplog} /tool e-mail send to="vvs@somewhere.com" subject="$[/system identity get name]-$[/system clock get time] Backup Configuration & Log - SBB-Optic" file=$files - - - - - - - - - Check size of scripts :foreach scriptId in [/system script find] do={ :local scriptSource [/system script get $scriptId source]; :local scriptSourceLength [:len $scriptSource]; :local scriptName [/system script get $scriptId name]; :put "$scriptSourceLength bytes: '$scriptName'" } [source, https://wiert.me/2017/06/08/mikrotik-scripting-language-a-list-of-questions-i-had-linking-to-the-forum-messages-having-answers/. Original source, https://forum.mikrotik.com/viewtopic.php?t=108405] - - - - - - - - - String handling What is the type of a variable? :put [:typeof $myVar] Deal with nonstring variable as a string: :if ([:tostr $myVar] = "something") do={:put "yes"} Replace a single character in a string of text with another See https://forum.mikrotik.com/viewtopic.php?t=40507#p627017 Get a string from a text file by position https://forum.mikrotik.com/viewtopic.php?t=85455 See also 'pick' above - - - - - - - - - ________________________________________________________________________________________________ Tricks - - - - - - - - - datetimefunction script at https://github.com/phistrom/datetime-routeros - - - - - - - - - Generate random numbers or strings (RouterOS =>7) [admin@MikroTik] > :put [:rndnum from=10 to=100] 37 [admin@MikroTik] > :put [:rndstr length=12 from="asdasdsafgwergqdgqerg"] ggeassdefasr - - - - - - - - - - - - Wait for procedure such as fetch to complete and indicate elapsed time Then display data obtained see https://forum.mikrotik.com/viewtopic.php?f=2&t=178355&p=878643#p878643 - - - - - - - - - - - - get interface list members from https://forum.mikrotik.com/viewtopic.php?t=166058 :foreach line in=[/interface list member print as-value where list=LAN ] do={:put ("interface is " . $line->"interface")} ________________________________________________________________________________________________ Bibliography 1. Script repositories. "MIKROTIK SCRIPT ROUTEROS DATABASE" at github. https://router-os.github.io/ Mikrotik Forum. "Useful scripts" https://forum.mikrotik.com/viewtopic.php?t=40507 Scripts by Grzegorz Budny. https://github.com/gbudny93/RouterOS_Useful_Scripts A search engine for routerOS scripting questions: https://router-os.github.io/ 2. Scripting Tips and Tricks https://wiki.mikrotik.com/wiki/Manual:Scripting_Tips_and_Tricks 3. Internal ID numbers, https://wiki.mikrotik.com/wiki/Manual:Scripting_Tips_and_Tricks#Do_not_use_console_numbers_to_get_parameter_values 4. Integer division with remainder added as decimal fraction, https://forum.mikrotik.com/viewtopic.php?t=11439#p534684 5. Examples in "Set custom MAC address on a bridge or interface" https://tikdis.com/mikrotik-routeros/hardware/mikrotik-commands/ ________________________________________________________________________________________________ ________________________________________________________________________________________________