Unexpected behavior when finding by variable value

I have this snippet:

{
    :put ("HARDCODED: " . [/ip firewall filter find comment="The unique comment"])
    :local comment "The unique comment"
    :put ("VIA VAR:   " . [/ip firewall filter find comment=$comment])
}

It returns:

HARDCODED: *25
VIA VAR: *43;VIA VAR: *1;VIA VAR: *2;VIA VAR: *3;VIA VAR: *4;VIA VAR: *5;VIA VAR:.... (and so on)

Why is that, why via $comment approach, I’m getting all the rules, not just the one with the comment?

comment=$comment ???

Is not one “Unexpected behavior”, it is always idiotic to call variables with the same name as fields, and this is true in all programming languages(*).

# code not fixed / optimized, except for the variable name
{
    :put ("HARDCODED: " . [/ip firewall filter find comment="The unique comment"])
    :local notanidioticname "The unique comment"
    :put ("VIA VAR:   " . [/ip firewall filter find comment=$notanidioticname])
}

comment=$comment
is like
“where item comment is = to the (same) item comment”, and obviously is true for all items, so all items are returned.

The solution is don’t use the same local variable name as the attribute. See https://wiki.mikrotik.com/wiki/Manual:Scripting_Tips_and_Tricks#Always_use_unique_variable_names

So using $comment would be it being nil/[:nothing], and find’s matcher with nil is ignore… so it returns them all. And it repeats the "VIA VAR: " multiple times is expected since that how arrays work with concatenation. See https://wiki.mikrotik.com/wiki/Manual:Scripting_Tips_and_Tricks#Be_careful_when_adding_array_to_string

Edit: See @rextended’s post above - same story.

Thank you!


Is it now?

Let's see:

Python:

the_parameter = "value"
def the_method(the_parameter):
    print(f"Value is: {the_parameter}")

the_method(the_parameter=the_parameter)

Ruby:

the_parameter = "value"
def the_method(the_parameter)
  puts "Value is: #{the_parameter}"
end

the_method(the_parameter: the_parameter)

Kotlin:

fun theMethod(theParameter: String) {
    println("Value is: $theParameter")
}

val theParameter = "value"

fun main() {
    theMethod(theParameter = theParameter)
}

C#:

using System;

class Program {
    static void Main() {
        string theParameter = "value";
        TheMethod(theParameter: theParameter);
    }

    static void TheMethod(string theParameter) {
        Console.WriteLine($"Value is: {theParameter}");
    }
}

Swift:

func theMethod(theParameter: String) {
    print("Value is: \(theParameter)")
}

let theParameter = "value"
theMethod(theParameter: theParameter)

PHP:

function theMethod($theParameter) {
    echo "Value is: $theParameter";
}

$theParameter = "value";
theMethod(theParameter: $theParameter);

Scala:

object Main extends App {
    def theMethod(theParameter: String): Unit = {
        println(s"Value is: $theParameter")
    }

    val theParameter = "value"
    theMethod(theParameter = theParameter)
}

PowerShell:

function TheMethod {
    param([string]$theParameter)
    Write-Host "Value is: $theParameter"
}

$theParameter = "value"
TheMethod -theParameter $theParameter

R:

theMethod <- function(theParameter) {
  cat("Value is:", theParameter, "\n")
}

theParameter <- "value"
theMethod(theParameter = theParameter)

Lisp:

(defun the-method (&key the-parameter)
  (format t "Value is: ~A~%" the-parameter))

(let ((the-parameter "value"))
  (the-method :the-parameter the-parameter))

Ada:

with Ada.Text_IO; use Ada.Text_IO;

procedure Main is
    the_parameter : String := "value";

    procedure The_Method (the_parameter : String) is
    begin
        Put_Line ("Value is: " & the_parameter);
    end The_Method;
begin
    The_Method (the_parameter => the_parameter);
end Main;

All seem to work just fine... But I guess, you learn something new every day :person_shrugging:

LOL, Lisp and Ada examples. Now, RouterOS’s logic inherits some from LUA actually, which ver 5(?) supported. I think they created the current language to be more “config centric” than a general-purpose language. All this still has to fit in 16MB flash disks…

If you think all commands are functions, the restriction makes more sense. In a routeros function, named parameters become local variables. So when [find] “function” executes, it has a local $address inside it. And same happens with user functions:

:global fn1 do={
   :return $address
}
:put [$fn1 address=1.1.1.1]
#output: 1.1.1.1

vs.

:global fn2 do={
   :local address
   :return $address
}
:put [$fn2 address=1.1.1.1]
#output:  (nothing)

I see, thank you for the insight. I’ll be extra careful in future and be aware of the scopes/overshadowing behavior - one can easily shoot themselves in the foot because of all of this.

(*)
idiotic != allowed
idiotic <> allowed

internal code for /ip add find where address=$address
(eval /ip address find
where=$address;$network;$netmask;$broadcast;$interface;$actual-interface;$invalid;$dynamic;$disabled;$comment;$.id;$.nextid;$.dead;(= $address $address);5)
(= item1 item2) is a function that return true is both item have same value.
In this case (= $address $address), so the function return true everytime for obvious reason.


internal code for /ip add find where address=$xxx
(eval /ip address find
where=$address;$network;$netmask;$broadcast;$interface;$actual-interface;$invalid;$dynamic;$disabled;$comment;$.id;$.nextid;$.dead;(= $address $xxx);5)
This time the address is really compared with $xxx that do not exist in this ambit and is searched on local (then global if not finded) variables.