I fully agree with that!I was about to post what I thought was important, then realized boen_robot has written everything I was going to
So +1 to everything he said..
I have been thinking about polling SNR/CCQ of radio links and tuning BGP parameters using the results.SNMP-GET
I would like to poll external devices and have based on the results change routes, enable/disable interfaces.
It would be a nice feature.
[admin@MikroTik] > :put (192.168.88.10 & 255.255.0.0)
192.168.0.0
[admin@MikroTik] > :put (2003:cf:2f1e:5c00:d250:99ff:fec0:d180 & ffff:ffff:ffff:ff00::)
Script Error: cannot compute bitwise "and" of internal number and internal number
Missing from being implemented or missing in my command?bitwise operator works, whats missing is :toipv6
:put ([:toip6 2003:cf:2f1e:5c00:d250:99ff:fec0:d180 ] & [:toip6 ffff:ffff:ffff:ff00::])
mrz, can you give advise?Missing from being implemented or missing in my command?bitwise operator works, whats missing is :toipv6
... does not give an error, but does not give output neither.Code: Select all[admin@MikroTik] > :put ([:toip6 2003:cf:2f1e:5c00:d250:99ff:fec0:d180 ] & [:toip6 ffff:ffff:ffff:ff00::])
I mailed support and they told me "to be aware of the issue, perhaps it will be fixed in the upcoming versions" (see Ticket#2018061222006824).
:put [:typeof [:toip6 ffff::/64]]
nil
+1 to this, my implementation is limited and awfulurl_encode();
You may paste the script into Terminal after "{" - that will prevent it from running line by line. Then "}" and Enter to execute.I'd like to have a debug function that helped with basic syntax. When writing code I spend most of my time getting the basics in place. I write my logic then comment most of it out then uncomment line by line to make sure each line is working. With ros code, if it doesn't work, you get nothing to say where the error is
Looks like "+" works just fine:- Ability to add items to end of arrays
{
:local a {1;2;3};
:set a ($a + 5);
:foreach v in=$a do={
:put $v
};
}
Not sureAre they mentioned in the wiki?
I didn't get what's wrong with dates, sorryAny thoughts on the dates?
No, the documentation says using a comma is the correct way:Looks like "+" works just fine:- Ability to add items to end of arrays
:set a ($a, 5);
What scripting functions do you mean?..Secure DNS implementation would be welcome in the future
What is the progress on the original topic? Has it been decided if this is going to happen, when, and what functions?Completely unrelated to original topic.
What topic is unrelated?Completely unrelated to original topic.
({'.id': '*213', 'address': '192.168.10.57', 'mac-address': 'F5:DE:F1:24:DC:20', 'client-id': '1:f0:de:f1:23:dc:2a',})
Please stop the off-topic messages, and please delete them if they are yours.I'd love to see the terminal object-oriented.
After re-reading my post I have to fully agree. Edited.Please stop the off-topic messages, and please delete them if they are yours.I'd love to see the terminal object-oriented.
(I will delete this when that happens)
/put ("13:14:15" +1w2d23h59m59s)
1w3d13:14:14
/system scheduler add name=SiBTemp on-event={nothing } start-date=feb/31/2019;
put [/system scheduler get [find name=SiBTemp] start-date]
mar/03/2019
interface wireless spectral-history wlan1 duration=2s
:varname [:caps-man remote-cap get $i serial]
/env print
BF090FS8938={foo="bar"; foo; bar}
You could put this into an array...hi,
possibility to create variables named from object on the routeur like :
so i have a variable named BF090FS8938 (serial number of the router)Code: Select all:varname [:caps-man remote-cap get $i serial]
Code: Select all/env print BF090FS8938={foo="bar"; foo; bar}
[admin@mt] > :global RemoteCap [ :toarray "" ];
[admin@mt] > :set ($RemoteCap->[ /caps-man remote-cap get $i serial ]) {"foo"="bar"; "bar"="testing..."}
[admin@mt] > :put ($RemoteCap->[ /caps-man remote-cap get $i serial ]->"foo")
bar
[admin@mt] > :put ($RemoteCap->"BF090FS8938"->"bar")
testing...
{
global replaceCharacterFunc do={
while condition=[find $1 $2 0] do={
set $1 ("$[pick $1 0 ([find $1 $2]) ]".$3."$[pick $1 ([find $1 $2]+1) ([len $1])]")}
return $1
}
# Replace a character with an other character
put [$replaceCharacterFunc "Can-t-be-used-as-name-for-variable" "-" "*"]
# Remove unwanted character(s)
put [$replaceCharacterFunc "Can-t-be-used-as-name-for-variable" "-"]
}
while condition=[find $1 $2 0] do={
{
global mreplaceCharFunc do={
:local n [:len $2]
while condition=[find $1 $2 0] do={
set $1 ("$[pick $1 0 ([find $1 $2]) ]".$3."$[pick $1 ([find $1 $2]+$n) ([len $1])]")}
return $1
}
}
:set $a [$mreplaceCharFunc "this @@@ @@ @ test of replacing multiple char(s)" @@ is]; :put $a
this is@ is @ test of replacing multiple char(s)
/interface wireless set [find default-name=wlan1] scan-and-connect = based-best-signal-ssid = [find=IspXSectorY];
/interface wireless set [find default-name=wlan1] set nv2-mixed-background-scan-from-all=enabled nv2-mixed-background-scan-time = 04: 00am
I prefer scsh.Just give us a bash
You can always start such a project or join an existing one, e.g. on github.To me the singularly most important addition would be opening this up to the community so that they could create and share functions safely and easily. So that you can leverage all the developers to contribute to a user extensible library.
Just as said above, i think you have more chances with it on githubTo me the singularly most important addition would be opening this up to the community so that they could create and share functions safely and easily. So that you can leverage all the developers to contribute to a user extensible library.
I think the scripting language should be extended with a file open/read/write/close construct where you can open a file (a handle is returned), then read or write it in a loop either line-by-line or as fixed-size blocks (up to 4096 bytes), and then close it when finished. The total size of the file would be arbitrary (as long as it fits on the storage).A built-in function that broke up files and variables into 4096 byte chunks would be extremely useful.
:local result [/tool fetch url=$url as-value output=user]; :if ($result->"downloaded" < 64) do={ :local data ($result->"data")
You mean, in here? https://wiki.mikrotik.com/wiki/Manual:S ... ime_errorsCatch error messages
You mean, in here? https://wiki.mikrotik.com/wiki/Manual:S ... ime_errorsCatch error messages
:global prefixLen do={ return [:tonum [:pick $1 ([:find $1 "/"]+1) [:len $1] ] ] }
/ip route print detail where active and bgp and [$prefixLen $"dst-address"]>24 and [$prefixLen $"dst-address"]<32
You show a scripts without descripton what they do... I check code and no details even in it.While we wait for the builtin functions:
https://github.com/merlinthemagic/MTM-R ... -Scripting
Can manipulate strings, files, md5 hashing, create GUIDs etc.
Try the documentations folder, the vast majority of functions / tools are documented......
You show a scripts without descripton what they do... I check code and no details even in it.
If someone must try your all scripts and build own info what each script do...then I even not try use them.
Miał,Try the documentations folder, the vast majority of functions / tools are documented.
While we wait for the builtin functions: https://github.com/merlinthemagic/MTM-R ... -Scripting. Can manipulate strings, files, md5 hashing, create GUIDs etc.
+1 from me.THISNot sure if already suggested, but JSON support would be absolutely amazing!!!!!
See: JParseFunctions for a kinda hacky (but extremely useful) workaround that really needs to be implemented natively!
any other HTTP client even RouterOS fetch tool.
:tojson
:jsonparse
{
"topkey": "topvalue",
"children": {
"oldest": "Jimmy",
"youngest": "Jane"
}
}
:jsonparse
:local jsonarr [:jsonparse [/tool/fetch ... as-value]]
:put $jsonarr
children=oldest=name=Jimmy;youngest=name=Jane;topkey=value
:put ($jsonarr->"children"->"oldest"->"name")
Jimmy
local a ( 10 / 3 ); log info $a
:local targetDate [$addDate date=[/system clock get date] days=3]
:local targetDate ([/system clock get date] + [:todate "3d"])
:do {
# things
} on-error e do={
:put "Failed, reason: $e"
};
1) [...] string parsing and string manipulation. [...] 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.
: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
:global replacedstring [:regex find="blah" replace="hello" from=$mystr]
:put [:typeof $replacedstring]
# string
Yup this comes up which then requires more complex conditional logic to avoid needing it...2) Next, the loops in ROS scripting are completely missing functionality to affect their code-flow.
:break - break out of a :for and :foreach, etc. loops
:continue - the usual concept of :continue in loops
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.3) Time and date manipulation. This is just not handled at all.
# returns the "7th day of last month"
bash> gdate --date "-1 month" +"%Y-%m-07"
# 2021-12-07
[user@ROS]> :datestr date="-1 month" format="+\"%Y-%m-07\""
# 2021-12-07
4) Finally, I would really like to get access to why an error occurred in on-error blocks. Here is what I would love:
Code: Select all:do { # things } on-error e do={ :put "Failed, reason: $e" };
4) Finally, I would really like to get access to why an error occurred in on-error blocks. Here is what I would love:
Code: Select all: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 .
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 theThe 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).
\(evl(...
import verbose=yes
Ideally with JSON to/from ROS array support.JSON encode and parse please!
For language syntax errors that are detected while compiling the script the line number when it is encountered is known.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 theThe 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).stuff on save/:set. But in the context of an "import", ROS seem to know the context of the line number being executed.Code: Select all\(evl(...
Declare your functions in a script, then execute that script from another script to "include" necessary functio(s): https://wiki.mikrotik.com/wiki/Manual:S ... #Functionso need to add include function.
/file add name="user.pub" content="ssh-rsa XXXXXXXXXXXXXXXXXXXXX"
Please fix the "third function call" error at last. For example, we have a function: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.
:local myFunc do {:log info "Hello world !"}
[$myFunc]
[$myFunc]
$myFunc
$myFunc
[$myFunc]
[$myFunc]
[$myFunc]
/system/script/myScript1/run script1
/system/script/myScript2/add script2
/system/script/myScript1/remove script1
: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 " }
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.Filesystem
Some basic file and directory handling like mkdir, I work around that using /tool fetch but would prefer not to have too.
Hi msatter@mike548141 When you have commands that could not work on every device then you can avoid errors by using do {} on-error={}
Code: Select all:do { /system/hardware } on-error={ :error "error: script not executable on this device " }
Code: Select all:do { /system/hardware } on-error={ :log "error: script X not executable on this device " }
:do { /system/routerboard/print } on-error={ :error "error: script not executable on this device " }
syntax error (line 1 column 26)
:global fsynnav do={
:local lsyncmd [ :parse $cmd; ];
:do {
:return [ $lsyncmd; ];
} on-error={
:put ("Syntax handler had a problem running a command, this hardware or RouterOS version probably does not support the command " . $cmd);
};
};
$fsynnav cmd="/system/routerboard/print"
Thanks Amm0 I had not noticed that. Going to amend my script now! As you say thats better than /tool/fetchThere 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.Filesystem
Some basic file and directory handling like mkdir, I work around that using /tool fetch but would prefer not to have too.
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").
:if ($something = "abc") do={
:put "Thing 1";
} elif ($something = "def") do={
:put "Thing 2";
} else={
:put "No Things here";
};
switch ($day) do={
case 0 do={
:put "Sun";
};
case 1 do={
:put "Mon";
};
case 2 do={
:put "Tues";
};
};
What???????????????Waiting for execution to complete
Some commands take a moment to finish executing (e.g. /system/check-installation) and the script doesn't know when its complete. I work around that using :execute and monitoring the file output but perhaps an exit status or something?
Thanks!
Other than /container start/stop, most command are synchronous (or can be made so). So the /system/check-installation just needs an "as-value":Waiting for execution to complete
Some commands take a moment to finish executing (e.g. /system/check-installation) and the script doesn't know when its complete. I work around that using :execute and monitoring the file output but perhaps an exit status or something?
:if (([/system/check-installation as-value]->"status")~"ok") do={
:put "good"
} else={
:put "bad"
}
@> /system/hardware print
bad command name hardware (line 1 column 9)
@> /system/hardware/print
syntax error (line 1 column 17)
/system/routerboard print
:global Dmsg ""
:global Gstop do={:put "";:put "*** INTERROTTO: $Dmsg ***";/co cl;:delay 10s;:put "";/quit}
:put "Controllo Installazione in corso..."
:global Gchkinst [/sys check-installation as-value]
:put "... Controllo Completato"
:if (($Gchkinst->"status")!="installation is ok") do={$Gstop Dmsg="INSTALLAZIONE CORROTTA: $Gchkinst"}
/sys back save dont-encrypt=yes password="" name=testmemory
:delay 4s
:if ([:len [/log find where message~"error creating backup file"]] > 0) do={
/file remove [find where name~"testmemory.backup"]
$Gstop Dmsg="ssh-host-key, NAND o FLASH memory danneggiata. Tentare risoluzione con: /ip ssh regenerate-host-key"
}
:execute "/sys routerboard reset-button set enabled=no hold-time=0s..1m on-event=\"\""
:execute "/int bridge port-controller set bridge=none cascade-ports=\"\" switch=none"
:execute "/int bridge port-extender set control-ports=\"\" excluded-ports=\"\" switch=none"
@merlinthemagic7 thx for the nice compilation of missing basic functionsWhile we wait for the builtin functions:
https://github.com/merlinthemagic/MTM-R ... -Scripting
Can manipulate strings, files, md5 hashing, create GUIDs etc.
##trim string example
:local strTool [($MtmFacts->"get") "getTools()->getTypes()->getStrings()"];
:local myStr " My string with leading and traling spaces and line breaks and chr returns \n\r";
:put [($strTool->"trim") $myStr]; #string "My string with leading and traling spaces and line breaks and chr returns"
:set ($s->"trim") do={
:global MtmFacts;
:local cPath "MTM/Tools/DataTypes/Strings.rsc/trim";
:if ([:typeof $0
...
but it is worse to return useless errors, if not requested.the nil value is a pain
# how is possible, at this pont, that $r0 is nil ???
:if ($r0 = [:nothing]) do={
#always return string, the nil value is a pain
:set r0 "";
}
:local p0 [:tostr $0];
:if ([:typeof $0] != "str") do={
:error ($cPath.": Input has invalid type '".[:typeof $0]."'");
}
Just found out aboutI'd love $0 to return the script name. This definetly needs to be a RouterOS functionality as one cannot work around on its own.
(Use case would be similar to busybox where one has one binary which behaves differntly depending on the name of the symbolic link used to call it.)
One could use e.g. the same script to stored by different names to handle various DDNS services.
:jobname
Hi PackElend,@merlinthemagic7 thx for the nice compilation of missing basic functionsWhile we wait for the builtin functions:
https://github.com/merlinthemagic/MTM-R ... -Scripting
Can manipulate strings, files, md5 hashing, create GUIDs etc.
I'm trying to understand the principle structure, reading:Instead of writing single functions you put all in a big array and call the function an array item?
- https://github.com/merlinthemagic/MTM-R ... e-examples
- https://github.com/merlinthemagic/MTM-R ... trings.rsc
That is how In understand these extracts:
andCode: Select all##trim string example :local strTool [($MtmFacts->"get") "getTools()->getTypes()->getStrings()"]; :local myStr " My string with leading and traling spaces and line breaks and chr returns \n\r"; :put [($strTool->"trim") $myStr]; #string "My string with leading and traling spaces and line breaks and chr returns"
Code: Select all:set ($s->"trim") do={ :global MtmFacts; :local cPath "MTM/Tools/DataTypes/Strings.rsc/trim"; :if ([:typeof $0 ...
/import flash/MTM/Facts.rsc;
:global MtmFacts;
##trim string example
:local strTool [($MtmFacts->"get") "getTools()->getTypes()->getStrings()"];
:local toolFact [($MtmFacts->"getTools")];
:local typeFact [($toolFact->"getTypes")];
:local strTool [($typeFact->"getStrings")];
:put [($strTool->"trim") " my string with spaces at each end "]; ##trims the leading and trailing spaces
:put [($strTool->"getRandom") 64]; ##gives you a random string of length 64
:put [($strTool->"toLower") "MY strIng WiTH mixED cases"]; ##my string with mixed cases
:local toolObj [($MtmFacts->"get") "getTools()->getHashing()->getSHA256()"];
:local myStr "My string";
:put ([($toolObj->"get") $myStr]); #3f9a07d83c604dba400d13df4d34566b78338804f0b3181d4e02089fe4daa7b0
I'd love $0 to return the script name.
I've thought before that $0 should be a "this" pointer when a function is contained within an array. In array function, $0 is the first parameter so this has side-effects (see viewtopic.php?t=181142)But in essense you are correct the functions are stored as items in an array.
The basic design is to attempts to mimic objects and having the ability to call methods on those objects.
Hi Amm0,I'd love $0 to return the script name.
It's this case where $0 being the containing array from a function be useful, :jobname does NOT help here:I've thought before that $0 should be a "this" pointer when a function is contained within an array. In array function, $0 is the first parameter so this has side-effects (see viewtopic.php?t=181142)But in essense you are correct the functions are stored as items in an array.
The basic design is to attempts to mimic objects and having the ability to call methods on those objects.
Since V7 copies arrays, if functions could access data within their OWN array, that the essence of an object.
But there is no way for a function to know what array contains it (e.g. where is "lives"), so no way to access data from other array members without some external variables.
Whether that's $0 or some $this available to "functions inside array's" is less important, it's that array function cannot access other array members directly is what limits more "real" objects (or even a C++-like vtable with more tricks).
:global MtmFacts;
:global MtmTools;
:set ($MtmTools->"hashing") $s;
/system/script/environment/print
Hi,As i explained earlier, it is necessary to chop up the globals or you get corruption. Point is you dont have to worry about that as long as you use the factories to load.
$temp
In the spirit of maintaining some type of name space separation, all Globals are prefaced with "Mtm". If $temp is being overridden, its not MTM doing it.Hi,
I'm wondering which global variable the scripts are using.
Is there any variable that may call, which is very likely that I use it in my self-developed scripts.Code: Select all$temp
[dcnstaff@xxxxx.xxxxx.xxx] > /system/script/environment/print
Columns: NAME
# NAME
6 MtmFaObjs
7 MtmFacts
8 MtmId
9 MtmIds1
10 MtmIds2
11 MtmLocks
12 MtmModelIfs1
13 MtmModels
14 MtmToolJson1
15 MtmToolParse1
16 MtmToolTime1
17 MtmToolTypes1
18 MtmTools
GREATIn the spirit of maintaining some type of name space separation, all Globals are prefaced with "Mtm". If $temp is being overridden, its not MTM doing it.