The topic has been discussed many times, but everything is forgotten when there is little practice…
Is it possible to access a variable whose name is not known in advance to the script function by passing it in a parameter? Something like this…
In other words, Question: is how to call a function whose name is set by a parameter of another function? Or is this not possible because at the time of execution of :parse, the function declared in :global has not yet been defined? Is there any way around this problem?
Hello Rex. I don’t know what can be explained well here. I am writing a global function that should receive data from other global functions, the names of which would be passed to the first one as parameter $1, a $2 to be the parameter passed to the second function. Having received data from other functions, the main one can do whatever it wants with it…
All my desire is expressed in the lines above, the meaning of which is absolutely clear to you, like a pro. Their syntax is of course not correct, I don’t know how to write it…
:global myFunc do={
:local Ans [[:parse ":global $1; return [$1 $2]"]]
:set Ans ($Ans+5)
:return $Ans
}
[$myFunc Func1 Parametr1]
Given that it seems like xxxxx to me, unnecessary complications,
it’s done like this:
:global addten do={
:local input [:tonum $1]
:return ($input + 10)
}
Yes. And same generally approach: string interpolation to dynamically create a command.
Here the use more complex. @rextended and @Sertik are trying to get around the restriction that a function must declare any global variables to be able to use them. So the :parse (aka eval()) is used to create a new scope to run a global function provided by a string name (not variable)
@patrikg asked likely because it common wisdom that “eval() is bad”. @rextended expresses that as “seems like bullshit to me”.
IMO using an RouterOS array to store functions is a better approach (akin to a C++ vtable) than use weird :parse tricks to dynamically call functions from other function. Especially since in V7 there are lot more restrictions of sharing global variables (e.g. netwatch etc script) and could be more in future.
Thank you so much, Rex! This is not nonsense, but a very necessary thing for me! That’s the line I was waiting for from you and you helped me a lot in this.:
In this way, you can call functions whose names are not known in advance, that is, they will be determined during the script !
Let me try to explain: imagine that you have a certain process (function) that, in order to solve its tasks, must and can, depending on the initial conditions, use several different functions-solutions to this problem. That’s the way out. The main function will, based on the conditions, call different handler functions, passing them the same parameters.
I used the Rex hint and now everything works for me the way I wanted it to! I’m happy !
Yeah, that’s a good one-liner. Here’s another neat trick if you want to call system scripts with arguments. This also works with “[/file get /dirname/scriptname contents]” if you prefer to store your scripts in a different location. Use “” as shown above if you need to evaluate at runtime.
[[:parse [/system script get scriptname source]] $arg1 $arg2 varname=$varname ]
It just be nice if you could have some “stored functions” since they could just be saved in config like a script. e.g. “/system/scripts/function add addVLAN source={}” so NO :global be need. Without resorting the @Larsa’s nifty but ugly approach.
As a “config language”, functions be more generally useful if folks didn’t need to result to weird tricks to effectively use them (e.g. scoping, :globals & :parse). Rather if functions were part of the config, it be easy to “share” function that do config things.
I actually like the nifty tricks folks use. And get why @Sertik is trying here. Just my problem is these nifty tricks get tricky if this a production router… Anyway Functions are useful, but annoying they are hidden beyond all the cruft to use them effectively.
# declaring a variable with substituting a value from Layer7 for it
[[:parse ":global $vname [:to$vtype $[/ip firewall layer7 get [find where name=$vname] regexp]]"]]
# Hack with array sorting (by Rextended)
:set hackSort ($hackSort, [[:parse "[:toarray {\"$ymd\"=\"$filename\"}]"]]);
# passing parameters to the script and running it
[[:parse "[:parse [/system script get $msgTxt source]] $Parametr1 $Parametr2"]]
# or the same thing, but with multithreading with parameter passing
:set result "[[:parse [system script get $calledFunctionName source]] ID=$queryID ChatID=$queryChatID]"
:execute script=$result
# or from the file
:execute script="[[:parse [/file get $FileName content]] ID=$queryID ChatID=$queryChatID]"
and there are many similar examples from forum scripts ...
Many authors have invested time and labor in creating such designs. There is a very good article in Russian by our esteemed Chupakabra (the author of the famous JSON parser for the Router OS), here is a link for those who are interested. The article examines the work in detail :parse, I highly recommend it to everyone:
As for the difficulties, I agree with Larsa and Amm0… I don’t know how from the point of view of implementation, but the scripting language of the OS Router does not allow you to directly write :global $Name or :local $Name, so that $Name itself is a variable, this generates many difficulties, of which we dodged in particular here…
Regarding the introduction of something new in functions, the type proposed by Amm0:
/system/scripts/function
developers should be careful. By introducing a new one, you should not violate the old features, as this will lead to the need to rewrite thousands of working scripts…
I also want to ask Rex. For example, there is a function in which I want to make a recursive call. We do it as usual like this:
:global MT do={:global MT; [$MT]; :put $0}
How can I call her without explicitly mentioning her name? That is, I want to make it so that if the function itself is renamed, the code does not have to be edited. I’m trying to do it like this:
As mentioned, RouterOS scripts are not a programming language.
Beyond a certain point things are useless and excessive.
Some things are excessive even if it were a programming language.
When will you ask for a script without any variable or function having a name,
and assigns the names of the variables and functions by reading them from a separate file?
In this case, I just wanted to make a recursive call to the function so that its name could be taken from $0, and not specified explicitly. This is necessary so that if someone other than me changes the name of this function, its operation will not be disrupted. Is this possible?