In RouterOS number is an integer.
Integer division yields an integer result. You can use the remainder operator, %, which yields the remainder after integer division. Thus, 10 % 3 yields 1.
In RouterOS number is an integer.
Integer division yields an integer result. You can use the remainder operator, %, which yields the remainder after integer division. Thus, 10 % 3 yields 1.
JSON encode and parse please!
What I remembered. I will add the topic to bookmarks, I will add it in fact when I write the next script.
By the way, when will you expand the window for inserting scripts through WinBox? Many of my scripts do not fit in it and I go through â7 circles of hellâ to add⌠o need to add include function. Time passes, memory is added, processors are more powerful, but the limitations are the same âŚ
:strleft - return a number of characters of the string (from the left)
:strright - return a number of characters of the string (from the right)
:strreplace - replace some characters in string
These would be super useful to avoid having to deal with "find and :pick all the time for simple string manipulation. And replace is just missing completely.
:break - break out of a :for and :foreach, etc. loops
:continue - the usual concept of :continue in loops
:local targetDate [$addDate date=[/system clock get date] days=3]
I would much prefer a :todate function which would format a string as a date, and allow for easy operations. Something like this working natively would be lovely:
:local targetDate ([/system clock get date] + [:todate "3d"])
:do {
# things
} on-error e do={
:put "Failed, reason: $e"
};
Replace and remove in string: http://forum.mikrotik.com/t/built-in-function-library/117288/63
To follow up on @tomaskir great suggestionsâŚ
One alternative to adding a bunch of new :str_X_ things, is full support regular expressions, specifically capture groups and replace. Thought is a â:regexâ command might be able to encompose a lot of use cases. Idea being it would return an ROS array with the âmatchesâ returned from the regex groups defined in the match= regular expression provided, based on data in from=
:global mystr "blah, blah, blah"
:global matches [:regex match=/blah/ from=$mystr multiline greedy global]
:put [:typeof $matches]
# array
:put $matches
# blah;blah;blah
:put [:len $matches]
# 3
For string substitution,
:global replacedstring [:regex find="blah" replace="hello" from=$mystr]
:put [:typeof $replacedstring]
# string
Basically have some light version of âsedâ and âawkâ be nice. Regex isnât for the faint of heart, but neither is BGP which supports regex in V7. Thinking it at least be cleaner script code than â[:strright [:strleft $x] 3â .
Yup this comes up which then requires more complex conditional logic to avoid needing itâŚ
Here perhaps another possible simplification is a wrapper around GNU date, so perhaps â:datestrâ that wraps it? The âmodernâ date in Linux support some humanizing options that pretty handy in /bin/sh script, see this gdate cheatsheet for example.
The options just follow the man pages, except with the ROS â=â method, so follow Bash shell usage a Linux date:
# returns the "7th day of last month"
bash> gdate --date "-1 month" +"%Y-%m-07"
# 2021-12-07
be the following in ROS, with added format=
[user@ROS]> :datestr date="-1 month" format="+\"%Y-%m-07\""
# 2021-12-07
As more sophisticated version of â:datestrâ also have an âas-valueâ that get the current Mikrotik âtimeâ type (e.g. weeks ago from 1970-epoch time).
- Finally, I would really like to get access to why an error occurred in on-error blocks. Here is what I would love:
:do {
# things
} on-error e do={
:put âFailed, reason: $eâ
};
>
That's a good one, and since "$e" likely be a string, a "_simple_ string find" operation from your 1 above is also needed with it :wink:.
Yes, $e could be a string. RouterOS commands already output an error message (string) when they fail, having access to that would be lovely for debug purposes. You could however easily imagine a more complex usecase. $e could be a more elaborate object, which could give you access to more failure data. For example:
$e->errorMessage - contains the error message string
$e->command - command which caused an error, useful if multiple commands in :do block
$e->lineNumber - on which line in the script the issue occured
etc.
The problem with line/pos number reporting is that all scripts are first compiled into a P-code (a stack language) which is then executed.
By the time an error occurs in some function, the processor does not know to what source line this corresponds (both text and linenumber).
Not to be pedantic, but thatâs not quite true in all contexts. Certainly System>Script/Schedule and functions created using :global get converted into the
\(evl(...
stuff on save/:set. But in the context of an âimportâ, ROS seem to know the context of the line number being executed.
import verbose=yes
Now even that doesnât help with functions. An error in a function, will only show the line number of the calling location, not the line number of the error with the functionâs code.
But on-error with string â which seems readily knowable since itâs printed to terminal â be a step in the right direction.
Since itâs a new year, Iâll add another plea for
Ideally with JSON to/from ROS array support.
For language syntax errors that are detected while compiling the script the line number when it is encountered is known.
But when a syntactically correct script encounters a runtime error during execution, the line number (and character position) of the original source line isnât known.
It is like using a compiled language like C on a computer. When you compile the code, the compiler shows the line number of your syntax errors.
But when you run it and encounter an error like division by zero or dereferencing a NULL pointer, the line number isnât printed with the error.
Of course it would be possible to introduce âdebugging infoâ in the form of special instructions that maintain a âcurrent line numberâ register in the P-code interpreter that is printed when a runtime error occurs, but it is extra effort and it has not been done.
o need to add include function.
Declare your functions in a script, then execute that script from another script to âincludeâ necessary functio(s): https://wiki.mikrotik.com/wiki/Manual:Scripting#Functions
json parsing seems to be the consensus in here and definitely has my vote. or maybe on devices with more storage just give us the ability to use bash, curl & jq ![]()
While this seems like nascent, or at least low moving, topic⌠Did run into another missing built-in function: zip/unzip and/or tar/gzip support.
The container package seems internally deal with TAR files, so be nice if this was exposed for scripting. Basically be handy to some :compress and :deflate function. Or, perhaps an option to /file that do similar.
I too would love to see curl/jq as built-ins ![]()
Creating a new file and appending input from CLI. Adding a new public ssh key as file, and importing it to a user. Today it has to be done via SCP/SFTP, then imported.
For example:
/file add name=âuser.pubâ content=âssh-rsa XXXXXXXXXXXXXXXXXXXXXâ
We are considering to add commonly used functions as built-in.
What functions would you like to see?
For example, MD5hash, random number generator and so on.
Please fix the âthird function callâ error at last. For example, we have a function:
:local myFunc do {:log info "Hello world !"}
If we call it with the shell twice in a row, it will be executed three times!
[$myFunc]
[$myFunc]
Hello world !
Hello world !
Hello world !
If we make a call without a wrapper , this error does not occur:
$myFunc
$myFunc
Hello world !
Hello world !
If the function is called to perform :put, there will be gaps:
http://forum.mikrotik.com/t/frequent-empty-variables-in-scripts-turned-out-to-be-bug/168049/1
Interestingly, subsequent calls after the second are processed normally:
[$myFunc]
[$myFunc]
[$myFunc]
Hello world !
Hello world !
Hello world !
Hello world !
As for the built-in functions, I will refrain from speaking for now, but what I would like is that it would be possible to create sections in the script repository, so that it would be possible to store scripts not in a common huge list. After all, the number of scripts in the repository of a modern router can be impressive. And you could call it by specifying the path
/system/script/myScript1/run script1
/system/script/myScript2/add script2
/system/script/myScript1/remove script1
Yes, this will entail the need to rewrite many scripts, but now the situation is the best for introducing folders into the repositories, when many scripts are already being rewritten for Ros7. Yes, itâs not clear how to make scripts located in different sections see and be able to run each other ⌠But, probably, this could be somehow solved. this should have been done from the very beginning.
In the end, for the system, the scripts, as they were stored, can be stored in one common section, but for the user they could be seen in separate subsections / folders
It would also speed up script browsing in Winbox when outputting a section of say 15 scripts is much faster than outputting 500.
Some ideas
Syntax checking
A big one for me would not be a new function, but making scripting easier across everyting using the Mikrotik platform.
RouterOS on a CHR virtual device does not have /system/routerboard/ in the CLI so a script will give a syntax error when it trys to load the script into memory i.e. pre-execution. Similarly most routerboard hardware devices do not have /system/hardware/ so they will give a syntax error on load if that is referenced anywhere in the script file.
The script has logic to avoid running the wrong commands on certain devices but the script will not execute because it will fail syntax checking. Currently I get around this by parsing the commands in dispute so they do not get validated until they are executed, but frankly thats pretty ugly.
The simple answer to me (but there are probably better ideas) would be to have the same CLI structure irresepctive of the device. For example I can call :put [ /system/routerboard/get routerboard; ]; and get a false if it were a CHR.
Filesystem
Some basic file and directory handling like mkdir, I work around that using /tool fetch but would prefer not to have too.
Ta
@mike548141 When you have commands that could not work on every device then you can avoid errors by using do {} on-error={}
:do { /system/hardware } on-error={ :error "error: script not executable on this device " }
:do { /system/hardware } on-error={ :log "error: script X not executable on this device " }
Filesystem
Some basic file and directory handling like mkdir, I work around that using /tool fetch but would prefer not to have too.
There is now â/file/add name=â in the latest V7âs. Directories kinda fake in RouterOS as they are constructed from the â/â in the name= ⌠so still need to create some file. But /file/add avoids awkward syntax, still not âmkdirâ ⌠but better than /tool/fetch for this.
Now they could add a â/file/add name=mydir type=directoryâ that least get what looks to be a empty directory in winbox and /file/print. But⌠directory are automatically created whenever you need a file, without any need for them to exist (e.g. why no âmkdirâ).