So I got curious about where the trouble makers in my blacklist were coming from and tried something like this:
/ip firewall address-list> :foreach a in=[find where list=blacklist dynamic] do={:put [:resolve [/ip firewall address-list get $a address]] }
It’s supposed to walk through the dynamic addresses in my blacklist and if it can resolve them to a host name, print it. Sadly it bombed on the first address which wouldn’t resolve.
Now maybe I’m missing something, but I couldn’t figure out how to get a script to ignore an expected error and march on like a good soldier. This is a pretty normal thing to do in a script after all, since you’re often trying to determine exactly that, which of these are valid, or set, or can be found, or …
So you have to have 2 scripts, one that runs and does stuff and handles situation when some value is not received as expected, and other script does the job, that can fail. as a result, if second script fails, first one that called second script to do the job will continue.
OK, that gives me some ideas, but it seems really… clunky.
Two scripts to do a simple loop, just because one command could fail, seems to cry out for a bit of language support, like a “:try” or something, or maybe a flavor of command substitution that deals with errors.
It is really silly to have it break a script, and if that wasn’t enough, these breaks leads to memory leaks. So it is quite absurd that this hasn’t been dealt with yet, I resently found a post here on the forum that some kind of try/catch was being cosidered, but that was two years ago, so I wouldn’t hold your breath.
If it is because MikroTik is afraid of breaking excisting scripts (not a concern they usually have), I have two simple ways it could be done, add either a parameter (e.g. break-on-fail=no) that indicates whether or not you want it to break on fail or just return :type ‘nothing’, this is after all the behavior most would have expected in the first place, secondly a paramerter (e.g. address-on-fail=0.0.0.0), which would return a dummy address of your choice on fail.
Until a scripting solution comes along, there is other alternatives, use a DNS server with a default responce like OpenDNS, they return one of two adresses to unresolvable domains, it may not be pretty, but it works, and since you know (since ROS 5.0beta-something) can tell :resolve which DNS server to use, you don’t have to change your whole DNS infrastructure to OpenDNS.
Since the scripting language doesn’t even seem to provide a way to pass parameters to scripts without using global variables (I’d be delighted to learn otherwise) it’s especially annoying. To put the resolve into a “safe” wrapper you need to set a global for the input parameter, set another global for the output value, …
Given that the language already has scopes, parameters are just a tiny step.
[admin@MikroTik] /system script> run blacklist-names;
pool-109-191-246-40.is74.ru
87-126-71-139.btc-net.bg
57.88.178.61.dail.lz.gs.dynamic.163data.com.cn
dynamicip-46-0-145-205.pppoe.samara.ertelecom.ru
mx-ll-180.183.141-17.dynamic.3bb.co.th
ip136.hichina.com
failure: dns name does not exist
Last I checked, scripts run concurrently. They do not run like a function. When you execute a script from another script, the calling script does not wait for the called script to finish, but continues to run.
I would use this. I added the global variable “done” to both scripts. The calling script sets done to false, executes the other script, then waits for the called script to set done true before continuing.
You might want to put a count on the while(!done) loop to terminate it if the called script fails to set done true after several times through the loop.
“:execute script-name” - not documented in the manual on the wiki, but does seems to run the script in the background.
“/system script run script-name” - doesn’t seem to do that.
That just leaves the problem of synchronization… And why :resolve often returns nothing when nslookup (using the router as it’s DNS) gets a result…
I am aware that the “:execute” command is not documented, but it should be. It works really well. I’ve been using it since the V2.9.x era.
The synchronization is handled by the “done” variable. That is the “while not done, do nothing” loop. But you will need to add a loop count to that to break from the loop after a dozen or so iterations. Should be something like this:
By my tests, the scripts are run one command/loop iteration at a time in each running script. So it works like this:
execute command 1 in script 1
execute command 1 in script 2
execute command 2 in script 1
execute command 2 in script 2
…and on.
and did not see any problems. I may have just been at the point where the code on the screen all turns to meaningless squiggly lines, and forgot the “:print” part.
Yeah, mostly just grumbling about the gaps in the documentation, which can be very frustrating.
Yeah, understood that, it was how to limit the loop and whether to use “:delay” or just “:nothing”. (Which BTW, is also missing from the scripting docs…) The original loop would have waited forever when a lookup failure occurred.
The scripting language could really stand to have a better synchronization/sequencing mechanism too…
BTW - Miramar Beach in the Destin/FWB area of the redneck riviera? I used to spend way too much time down there on business. Just the names give me flashbacks to driving along 98 when somebody brought an 111 in off the water at full throttle and tree top level, stopping traffic in both directions and leaving the rental car swaying back and forth from the shock wave. It was like a scene from “Close Encounters”.
I would prefer the error handling do more than just abort the script if a command fails, but I don’t have a solution on how to do that. That part is up to Mikrotik.
It would be handy to “call” scripts (stop execution on main script until child script is complete) rather than just execute them. But there would need to be better error handling or it would fail also when the child script aborts.
Add: Unless the child script returns to the parent script on abort and started executing commands there again. I would be ok with that.
Like this
:global done
:set done false
:call myScript
:if(!$done) do={
// now you know it did not finish, but aborted.
// so do what you do on a fail
}
This would be myScript
:global done
// do the stuff that might abort here
// If it aborts, that is the same as "return false"
// then last thing (equivalent of "return true")
:set done true
And I agree that the wiki docs could use some updating.
Thanks for karma.
Yes, this is the “Redneck Riviera”. It is not as redneck as it was when I got here in 1986, but I know what you mean.
The military still flies around here, but since the area is no longer considered “sparsely inhabited”, the jets can’t fly that low here now. I was assigned to Fort Rucker in 1969, and flew Hueys and Chinooks up and down this beach. There was nothing here then.
:global return
:set return false
:global num
:put (" try to resolve #".$num)
/resolve [/ip firewall address-list get $num comment]
:set return true
main script
:global num
:local i
:local x 2
:for i from=0 to=$x step 1 do={
:set num $i
:put ("checking ".$num)
/sys script run [find name="catch_script"];
:if ($return) do={
:put ($num."= risolto");
} else={
:put ($num."= non risolto");
}
}
Result:
checking 0
try to resolve #0
0= risolto
checking 1
try to resolve #1
1= risolto
checking 2
try to resolve #2
failure: dns name does not exist
[admin@Morm1] /system script>
Does exiting on first error hold true for ssh executed scripts as well?
For example with
export file=foo.bar
?
As someone who is a complete newbie to Mikrotik scripting where do one best read up on the behavior of commands etc?
Are there builtin manuals, or documentation somewhere online that I am not finding?
Yes, an error will exit before the next line. If you don’t want that, then you need the “:do { /badcommand} on-error={};” syntax shown above around code that might cause an error (and where you’d still want rest of code to continue).
In terms of docs, https://help.mikrotik.com/docs/display/ROS/Scripting describes scripting syntax, and https://help.mikrotik.com/docs/display/ROS/Scripting+examples. Now…docs leave you hanging on how it works specific commands sometimes – like what can “throw” an error that exits, vs what returns an empty string/[:nothing]. e.g. The first case will stop a script from SSH, but not some “errors” manifest as an empty string/array. For each command is consistent is what they do for errors… but different operations/cmds sometimes have subtle differences in error handling.