find behaves in wierd ways if you pass it something like `domain="$domain"`

I couldn’t for the life of me figure out what is wrong with my code so I ended up creating this fun little debug script:

/ip dhcp-server network
:put "$domain"
:put [ :typeof "$domain" ]
:put [ :typeof ".lan" ]
:put [ find domain="$domain" ]
:put [ find domain=".lan" ]
:local somethingDifferent "$domain"
:put [ find domain="$somethingDifferent" ]

to which the output I got was:

.lan
str
str
*2;*1
*1
*1

I was convinced some black magic was happening since my variable that I set to the value .lan and literally the value .lan are giving different results.
I added the final lines naming the variable something else and, bam, it works…

I’ve checked this with other things as well like

:put [find comment="$comment"]

and it does just return every single ID, even if the variable is empty/undefined. It looks to me like find itself is setting these values internally and then running into some odd behaviour like this.

Honestly I’m not sure what is happening internally and why it works like this, but I really think it shouldn’t. The substitution should happen before or as soon as we enter the internal scope of “find”. I would expect variable substitution to always be identical to substituting the same value by hand, if I give something an argument I should not have to care about what variable names are used internally by that command/function.

Is this a known limitation, or is it a bug?

Without telling what you try to do, its hard to help.

To get anything correct out of RouterOS, you need to work with the id of an object.
Here is an example of getting some DHCP network information. (cut/past to terminal)

{

/ip dhcp-server network
:foreach id in=[find] do={
	:put "id=$id domain=$[get $id domain] address=$[get $id address] gateway=$[get $id gateway]"
}
}

Use Splunk> to log/monitor your MikroTik Router(s).–> MikroTik->Splunk :mrgreen:
Backup config to GmailBackup
Block users that tries ut use non open ports → Block

@mark0016

Is this a known limitation, or is it a bug?
Yes, is the bug of your limitation.

You do not provide any “script” that give that result.
You omit

:local domain ".lan"

(or similar) at the start
that is only knowed by you, and *1,*2 etc. can be displayed only on your device because only you have your device.


Have you discovered the “hot water”?

Nothing mysterious, as in all languages, you don’t have to use the name of a field or a reserved word as the name of a variable…
Logical and clear.

:put [find comment="$comment"]

Translated on human language…
Put (on screen) the results of the find about any item that have on comment the value of it’s own comment…

Since the comment is identical as the value of comment…


For example, for search any item that have comment length = 26:

:put [find where ([:len $comment] = 26)]

Can you name another language where parameter names are reserved keywords? Especially only within the context of the function call itself.

The only way this would be logical is if you could do something like

$this->comment

.

Sorry for the off-topic Rex, what about your link to your scripts?
I don’t see it in the signature.

All the signature, for abuse, are removed.

You can search “Rextended Fragments” or “Rextended Snippets”
http://forum.mikrotik.com/t/rextended-fragments-of-snippets/151033/1

It doesn’t matter I don’t want to argue about other languages, if the example is wrong or not,
but everyone should have common sense, not to call the variables with the usual name of the fields, then there is confusion.

On RouterOS it is like this, and here we are talking about RouterOS, not other languages.


Logical? For what? IS not clear what is it (and the right syntax if is wanted read/set the value is $this->“comment” or $this->$comment)


Is nice to discovery what happen if is used this:

:global x ({})
:set ($x->$x) "test"
:put ($x->$x)

Then don’t talk about it as something that’s a thing in “all languages”. The common sense would be to expect it to behave as all other languages and for variables not to get magically overwritten. It is very common to name variables the same as the parameter or field name.

I understand, thank you.

..

This is a known problem, though the exact explanation is not known. I asked Mikrotik support and they replied (in Ticket#2019010222000454):

This is how scripting works in RouterOS and we will not fix it.

I have started to use variable names in camel case. So instead of domain=$domain use domain=$Domain, which works as expected.

Is WANTED problem, not for create problems, but for use extended syntax on print and find, like my example,
on this way you can do operations on find, for not elaborate twice or more the results.


Some examples:

(on find)

# obtain identity from interface
{
:local int "ether5"
/ip neighbor
:foreach item in=[find where ($interface->0)=$int] do={
    :put [get $item identity]
}
}

Why: “interface” is one array, on this way only the 1st value of the array is parsed

(1st find)

/ip dhcp-server lease
:foreach id in=[find where [:typeof $"active-address"]="ip"] do={
    :local activeMAC    [get $id active-mac-address]
    :local if           "undefined"
    :local outstring    "undefined"
    /interface bridge host
    :local searchresult [find where mac-address=$activeMAC]
    :if ([:len $searchresult] > 0) do={
        :set if [get $searchresult on-interface]
        :set outstring "For bridge host table the $activeMAC is coming from $if"
    } else={
        :set outstring "Not find any result on bridge host table with MAC $activeMAC"
        /ip arp
        :set searchresult [find where mac-address=$activeMAC]
        :if ([:len $searchresult] > 0) do={
            :set if [get ($searchresult->0) interface]
            :set outstring "$outstring, but for ARP table $activeMAC is coming from $if"
        } else={
            :set outstring "$outstring, neither on ARP table."
        }
    }
    :put $outstring
}

Why: Return values only if “active-address” is one IP

Another example (on find)

/ip firewall address-list
add address=79.116.0.0/16 list=checklist
add address=86.127.0.0/16 list=checklist
add address=192.168.0.0/16 list=checklist

:local logIp 86.127.55.66

:if ([:len [find where list=checklist and (($logIp in $address) or ($logIp = $address))]] > 0) do={
    :put "$logIp found inside one, or more, address pool in checklist"
}

The way I think about this is that commands are functions, and the standard arguments are “named parameters” to it. And inside a (user-defined) function, the argument names are local variables, so kinda make sense that built-in commands (i.e. function-like) would follow similar scoping rules as user-defined functions.

Exactly

Try to paste this on terminal… :wink:

:put [:parse "/ip add find where true"]

(eval /ip address findwhere=$address;$network;$netmask;$broadcast;$interface;$actual-interface;$invalid;$dynamic;$disabled;$comment;$.id;$.nextid;$.dead;true;0)