ros code
:global myFunc do={ :put "arg a=$a"; :put "arg '1'=$1" } $myFunc a="this is arga value" "this is arg1 value"Output:
arg a=this is arga value
arg '1'=this is arg1 value
http://wiki.mikrotik.com/wiki/Manual:Sc ... #Functions
:global myFunc do={ :put "arg a=$a"; :put "arg '1'=$1" } $myFunc a="this is arga value" "this is arg1 value"Output:
arg a=this is arga value
arg '1'=this is arg1 value
/system script run myScript myVar=myValueAnd analogously for "/import" scripts?
OK, but... Could we hopefully get that in the (near?) future?You can't pass variables with run command. You will need to parse script into variable as shown in the examples.
:do { :put [:resolve test.com]; } on-error={ :put "resolver failed"}; :put "lala"
output:
resolver failed
lala
Currently no, but as a workaround you can add scheduler to run on startup script which will import all required scripts into :global variables.Similarly to the question before... can one :return from a script/import?
(Yeah, I really prefer abstracting away utility functions away, for re-usability's sake)
:global PutValue do={ :put $1 } :for i from=1 to=3 do={ :put $i $PutValue $i }2) Can you not define functions as :local? This should work, but doesnt:
{ :local PutString do={ :put function } :for i from=1 to=3 do={ :put $i $PutString } }
Thanks!tomaskir these problems will be fixed in next release.
{ :local sText "This is a sting of text." :global putString do={ :put $sText } $putString :put $sText }So since I cant at the moment define local functions yet, I have to keep all my variables and all my functions as global, which is a mess and creates potential for naming conflicts
:global sText "This is a sting of text." :global putString do={ :put $txt } $putString txt=$sTextSince there is a bug with local vars being passed. I am using global as an example
{ # local variable :local sText "This is a sting of text." # local function :local putString do={ :put $sText :set sText "Now a different string of text" } $putString :put $sText }As you said, you can acomplish this with passing an argument to the function, but if it also works this way, it gives us more options
{ :local curTime :local curDate :set curTime [/sys clock get time] :put [:typeof $curTime] # works great with compares etc :if ($curTime > "12:00:00") do={ :put "Its after noon" } else={ :put "Its before noon" } # also works great :put ($curTime + 1h) :set curDate [/sys clock get date] :put [:typeof $curDate] # doesnt work since type is str :if ($curDate > "jan/01/2013") do={ :put "We are after 2013" } else={ :put "We are before 2013" } # doesnt work since type is str :put ($curDate + 1d) }If a "date" type existed and was handled the same way as a "time" type, it would save me SO MUCH work in scripting. It would only take a couple of hours for a programmer to make this, but would save hundreds
You will never be able to access local variables (defined outside function) inside function. For that use global variables.As you said, you can acomplish this with passing an argument to the function, but if it also works this way, it gives us more options
I dont have to pass an argument and then return a value, all is handled with the variable, so less code to accomplish the same.
Understood, will either pass argument and return value, or use a :global.You will never be able to access local variables (defined outside function) inside function. For that use global variables.
Do you have an example of using the :return feature? I cannot find an example of it on the Wiki.Few more features:
* Now function can return value with :return command
* Catch run-time errorsros code
:do { :put [:resolve test.com]; } on-error={ :put "resolver failed"}; :put "lala"http://wiki.mikrotik.com/wiki/Manual:Sc ... ime_errorsCode: Select alloutput: resolver failed lala
I was more asking, can you actually do something with the returned value, or does it only put it on the console? Or in the case of a function, will it return to the main script without executing the rest of the called function?Return example added:
http://wiki.mikrotik.com/wiki/Manual:Sc ... #Functions
[admin@x86] > :global sum do={ :return ($1 + $2)} [admin@x86] > :put (14 - [$sum 3 4]) 7
:global lala [$sum 3 4]
With or without JIT compilation, it will always be simple to write bad code.Finally something to improve the scripting, but do you plan to implement JIT compilation of the scripts or some other means to lower the CPU utilization as well? It is needed because the scripting engine on ROS is killing the system performance - it is too simple to implement a script that is doing nothing, but CPU load is at 100%.
Having it be "simple" to write bad code is one thing... having it be unavoidable is another. And with ROS scripting, it's simple to think of scenarios where bad code is unavoidable.With or without JIT compilation, it will always be simple to write bad code.Finally something to improve the scripting, but do you plan to implement JIT compilation of the scripts or some other means to lower the CPU utilization as well? It is needed because the scripting engine on ROS is killing the system performance - it is too simple to implement a script that is doing nothing, but CPU load is at 100%.
[misha@test] > ip route print where 172.16.2.1 in dst-address dynamic Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit # DST-ADDRESS PREF-SRC GATEWAY DISTANCE 74 ADo 172.16.2.0/29 172.16.88.1 110when i convert this command to function, it not working
[misha@test] > :global route do={/ip route print where $1 in dst-address dynamic } [misha@test] > $route 172.16.2.1 Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit # DST-ADDRESS PREF-SRC GATEWAY DISTANCE [misha@test] >
+1. A true language would be nice.Having it be "simple" to write bad code is one thing... having it be unavoidable is another. And with ROS scripting, it's simple to think of scenarios where bad code is unavoidable.With or without JIT compilation, it will always be simple to write bad code.Finally something to improve the scripting, but do you plan to implement JIT compilation of the scripts or some other means to lower the CPU utilization as well? It is needed because the scripting engine on ROS is killing the system performance - it is too simple to implement a script that is doing nothing, but CPU load is at 100%.
If you run this, you can see that the passed parameter type is "str" (string) and needs to be converted to an IP.Hi dear Mikrotik Team,
I create function , and can't retrieve datasros code
[misha@test] > ip route print where 172.16.2.1 in dst-address dynamic Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit # DST-ADDRESS PREF-SRC GATEWAY DISTANCE 74 ADo 172.16.2.0/29 172.16.88.1 110when i convert this command to function, it not workingros code
[misha@test] > :global route do={/ip route print where $1 in dst-address dynamic } [misha@test] > $route 172.16.2.1 Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit # DST-ADDRESS PREF-SRC GATEWAY DISTANCE [misha@test] >
:global route do={:put [:typeof $1]; /ip route print where $1 in dst-address dynamic} $route 172.16.2.1You can do it this way when you call the function:
:global route do={/ip route print where $1 in dst-address dynamic} $route [:toip 172.16.2.1]Or put the code in the function itself:
:global route do={/ip route print where [:toip $1] in dst-address dynamic} $route 172.16.2.1
If you run this, you can see that the passed parameter type is "str" (string) and needs to be converted to an IP.Hi dear Mikrotik Team,
I create function , and can't retrieve datasros code
[misha@test] > ip route print where 172.16.2.1 in dst-address dynamic Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit # DST-ADDRESS PREF-SRC GATEWAY DISTANCE 74 ADo 172.16.2.0/29 172.16.88.1 110when i convert this command to function, it not workingros code
[misha@test] > :global route do={/ip route print where $1 in dst-address dynamic } [misha@test] > $route 172.16.2.1 Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit # DST-ADDRESS PREF-SRC GATEWAY DISTANCE [misha@test] >ros code
:global route do={:put [:typeof $1]; /ip route print where $1 in dst-address dynamic} $route 172.16.2.1You can do it this way when you call the function:ros code
:global route do={/ip route print where $1 in dst-address dynamic} $route [:toip 172.16.2.1]Or put the code in the function itself:ros code
:global route do={/ip route print where [:toip $1] in dst-address dynamic} $route 172.16.2.1
:global myFunc=do {
:put $args
}
$myFunc "1" "2"
1:1;2:2
Starting from v6.2 we have added new syntax which allows to define functions and even pass parameters to them
ros code
:global myFunc do={ :put "arg a=$a"; :put "arg '1'=$1" } $myFunc a="this is arga value" "this is arg1 value"Output:Read more in documentation:Code: Select allarg a=this is arga value arg '1'=this is arg1 value
http://wiki.mikrotik.com/wiki/Manual:Sc ... #Functions
:local fnArray;
:foreach f in=[/system script find where name~"^Function.*"] do={:set fnArray ($fnArray.",".[/system script get $f name])};
:set fnArray [:toarray $fnArray];
:foreach f in=$fnArray do={:exec script=":global \"$f\" [:parse [/system script get $f source]]"; /log info ("Defined function ".$f);};
/system script run "Functions"
:local testfunc $Functions
Starting from v6.2 we have added new syntax which allows to define functions and even pass parameters to them
ros code
:global myFunc do={ :put "arg a=$a"; :put "arg '1'=$1" } $myFunc a="this is arga value" "this is arg1 value"Output:Read more in documentation:Code: Select allarg a=this is arga value arg '1'=this is arg1 value
http://wiki.mikrotik.com/wiki/Manual:Sc ... #Functions
In case someone finds this useful, I've added this code to my start-up script to automatically define functions from system scripts that start with "Function" (or any other prefix that you may choose). Just creating a script called "FunctionWhatever" will automatically define a global function with the same name available from terminal session or from any other script (remember to declare the function in the script that will use it, though).
Enjoy!
Code: Select all:local fnArray; :foreach f in=[/system script find where name~"^Function.*"] do={:set fnArray ($fnArray.",".[/system script get $f name])}; :set fnArray [:toarray $fnArray]; :foreach f in=$fnArray do={:exec script=":global \"$f\" [:parse [/system script get $f source]]"; /log info ("Defined function ".$f);};
I have some examples of that here viewtopic.php?f=9&t=40507#p627016how do you call the Functions* after running this script? Do I have to useor there is another fancy way( for ex.Code: Select all/system script run "Functions"
)Code: Select all:local testfunc $Functions
You can do it if you use the older [:parse] functionality. I wanted a function to do some debug logging of a script, but only when I needed it.You will never be able to access local variables (defined outside function) inside function. For that use global variables.
# Declare variables to be used by $log
:local verbose true
:local ident "install"
# Create $log
:local logfn ""
:if (verbose) do={:set logfn ":log info \"$ident: \$1\";"}
:local log [:parse $logfn]
# Use $log
$log "Starting"
Is there a way to get an array of the unnamed/positional parameters?
This would make functions with a variable number of parameters possible.
Despite this thread's age, I use functions with positional/named parameters all the time in ROS script. This works great if the function is declared in global scope, but when a function is declared as a member of an array, things get more confusing/difficult.simply check inside the function if the parameter are passed with ":typeof $3", if the results are "nothing" the parameters are not provided, in this way you can create one function that produce different outputs based on parameters number.
been using for ages... never thought about that idea for checking number of things passed. useful for backward compatability. thankssimply check inside the function if the parameter are passed with ":typeof $3", if the results are "nothing" the parameters are not provided, in this way you can create one function that produce different outputs based on parameters number.
Do you think you could add perhaps pass parameters by reference (instead of building it that you return (limited to one variable). I know the answer may be "but then declare it as a global variable to access it". That does limit code reusability a lot. (One function to work on many things).Starting from v6.2 we have added new syntax which allows to define functions and even pass parameters to them
ros code
:global myFunc do={ :put "arg a=$a"; :put "arg '1'=$1" } $myFunc a="this is arga value" "this is arg1 value"Output:Read more in documentation:Code: Select allarg a=this is arga value arg '1'=this is arg1 value
http://wiki.mikrotik.com/wiki/Manual:Sc ... #Functions
I like @msatter's "just return an array" approach. But if the count of unnamed args is what's needed, this would work for most types (e.g. NOT key-values NOR array-of-arrays):I didn't understand, can you explain yourself better with an example?
:global "how-many-args-passed" do={
:local argv [:toarray "$1,$2,$3,$4,$5,$6,$7,$8"]
:put "I got $[:len $argv] unnamed arguments"
}
$"how-many-args-passed" one
# I got 1 unnamed arguments
$"how-many-args-passed" one two
# I got 2 unnamed arguments
$"how-many-args-passed" one two 3
# I got 3 unnamed arguments
$"how-many-args-passed" one two 3 four
# I got 4 unnamed arguments
:global FunctionTest do={
:local value1 $var1
:return $value1
}
:global value2 [$FunctionTest var1="test"]
:global FunctionTest
:global value2 [$FunctionTest var1="test"]
From prev. post:Yes, sure, I did. I said I execute it.
My code is not same code if you look closely.But, when I execute the same code via another script, the global variable value2 is always empty.
FWIW, this is covered by doc's "tips and tricks":But, when I execute the same code via another script, the global variable value2 is always empty.
:log info "CF-DDNS: Begin"
:local flDDNSCloudFlare do={
:local zoneid "xxxxxxxxxxx"
:local token "nxxxxxxxxxxxxxxxxxxxxxx"
:local ttl "60"
:local prox false
:local date [/system clock get date]
:local itime [/system clock get time]
:local now "$date $itime"
# Get System Identity #
:local SystemID [/system identity get name];
#define real current IP
:local IPNEW
:local vRequest
:if ($vLTE = true) do={/ip route enable [find comment="nslookuplte"];:delay 2;}
:log info "CF-DDNS: $vTypeIP $vType $vRecCFname"
:set vRequest [tool fetch url="https://$vTypeIP.icanhazip.com" mode=https output=user as-value]
:set IPNEW [:pick ($vRequest->"data") 0 ([:len ($vRequest->"data")]-1)]
:if ($vLTE = true) do={/ip route disable [find comment="nslookuplte"];:delay 2;}
:log info "CF-DDNS: Current IP for $vRecCFname : $IPNEW"
:local IPCUR
:set IPCUR [/tool fetch http-method=get mode=https \
url="https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$vRecCFid" output=user as-value \
http-header-field="Authorization: Bearer $token,Content-Type: application/json"]
:set IPCUR [:pick ($IPCUR->"data") 0 ([:len ($IPCUR->"data")]-1)]
:set IPCUR [:pick $IPCUR [:find $IPCUR "content"] [:find $IPCUR "proxiable"]]
:set IPCUR [:pick $IPCUR 10 ([:len $IPCUR]-3)]
:log info "CF-DDNS: Current Record for $vRecCFname : $IPCUR"
#update cloudflare
:if ($IPNEW != $IPCUR) do={
:log info "CF-DDNS: Public IP changed to $IPNEW, updating"
/tool fetch http-method=put mode=https \
url="https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$vRecCFid" output=user as-value \
http-header-field="Authorization: Bearer $token,Content-Type: application/json" \
http-data="{\"type\":\"$vType\",\"name\":\"$vRecCFname\",\"content\":\"$IPNEW\",\"ttl\":$ttl,\"proxied\":false,\"comment\":\"updated by script each 5 min $now from $SystemID\"}"
:log info "CF-DDNS: Host $vRecCFname updated with IP $IPNEW"
} else={
:log info "CF-DDNS: Previous IP $IPNEW not changed, quitting"
}
:log info "CF-DDNS: End"
}
:log info "CF-DDNS: NewNew"
[$flDDNSCloudFlare vRecCFid="abxxxxxxxxxxxxxxxxxxxx" vRecCFname="v.foo.foo" vType="AAAA" vTypeIP="ipv6" vLTE=false]
[$flDDNSCloudFlare vRecCFid="3cxxxxxxxxxxxxxxxxxxxx" vRecCFname="v4.foo.foo" vType="A" vTypeIP="ipv4" vLTE=false]
:log info "CF-DDNS: Last"
[$flDDNSCloudFlare vRecCFid="bdxxxxxxxxxxxxxxxxxxxx" vRecCFname="raffetlte.foo.foo" vType="A" vTypeIP="ipv4" vLTE=true]
{
:local func do={ :return [:nothing] }
:put "run test1"
:put [$func domainName=123]
:put "run test2"
:put [$func domainName=223]
:put "run test3"
:put [$func domainName=323]
:put "run test4"
:put [$func domainName=423]
}
{
:local func do={ :return [:nothing] }
:put "run test1"
:local void [$func domainName=123]
:put "run test2"
:local void [$func domainName=223]
:put "run test3"
:local void [$func domainName=323]
:put "run test4"
:local void [$func domainName=423]
}
{
:local func do={ :return [$domainName] }
:put "run test1..."
:local void [$func domainName=123]
:put "test 1 is $void, run test2..."
:log info [$func domainName=223]
:put "test 2 on logs, run test3..."
:local void [$func domainName=323]
:put "test 3 + 6 = $($void + 6)"
:put "all test done"
}
It's the [] sub-command that's the issue if I recall - if it's command result isn't going to a variable, there is no need for the [] backets.For avoid useless print, like