[SOLVED] Using Dynamic Variable Names

great :slight_smile:
may you explain why
[:parse ("global "vlan_common";:global testx ($"$([
is necessary?

"global "vlan_common"
→ (evl /globalname=$vlan_common)
what happens next?

in particular ($"$
as I would expect that
:put ( ( :parse ("$"vlan_common" ") ) )
gives the content of global “vlan_common”

All this is just for example, just to make your idea work, but is better do more “polite” code…

for example this is really hard to read and understand…

[:parse ("global \"vlan_common\";:global testx (\$\"$([:toarray "UP,vlan_common,KIDS"]->1)\"->\"name\")")]

something like this is better (example not tested):

:global readfromthis ([:toarray "UP,vlan_common,KIDS"]->1)
:global readthisfield "name"
[:parse ("global \"vlan_common\";:global testx (\$\"$readfromthis\"->\"$readthisfield\")")]

great :slight_smile:
may you explain why
[:parse ("global "vlan_common";:global testx ($"$([
is necessary?
Because you must declare the variable used or do not work

"global "vlan_common"
→ (evl /globalname=$vlan_common)
what happens next?
I do not go inside how internally the script are converted and run, if you obtain “evl” as results, probably you use “:parse” on wrong way
“:parse” must be executed, not readed or assigned…

in particular ($"$
as I would expect that
:put ( ( :parse ("$"vlan_common" ") ) )
gives the content of global “vlan_common”
again, “:put” can not read “:parse” as expected from you…

but

[admin@MikroTik] <SAFE> :put (  :parse ("$(vlancommon)") )
comment=COMMON SERVICES AND DEVICES / OFFICE;id=100;name=VLAN_COMMON

works

declaration is done in line one, so why doing it again?

because every time you “:parse”, is like you start another program,
what is do with previous “:parse” is lost
with “:global variablename” you instruct the system than you want use (already created or not) the variable “variablename”

when you write ":put (:parse ...)" on that way, simply the script convert vlancommon value (array) to "instructions" and the result is "comment...." (because internally the arrays are sort)
please do not give "but", only questions.

will do

I was able to proceed thx to your comments, some questions are remaining.
I will add code later or tomorrow (hopefully) with the full code

I do not want be unpolite “but” for me is hard to explain, I’m not english :slight_smile:

travelling was more time-consuming than expected but here we go, my configuration/script
I modified your code slightly:

  • keeping space works well and readability is maintained
  • some variables can be local scope but not all, whyever?
    # name the device being configured
  /system identity set name="AP+ES_GF_MT-hAP-ac3"

  # create one bridge, with defaults only
  /interface bridge 
    add name=bridge-VLANs
  
  # add VLANs
  /interface vlan
    add interface=bridge-VLANs   vlan-id=999   name=VLAN_MGMT   comment="NETWORK DEVICES MANAGEMENT VLAN"	

  # VLAN interface for L3 Management Access
  /ip address  
    add address=10.10.9.001/24   interface=VLAN_MGMT   comment="NETWORK DEVICES MANAGEMENT VLAN"    --> CR_2B_MT-CCR1009-8G-1S-1S+      		
    add address=10.10.9.002/24   interface=VLAN_MGMT   comment="NETWORK DEVICES MANAGEMENT VLAN"    --> CS-PoE_2B_MT-CRS328-24P-4S+

  # The route to the router to allow L3 communication. 
  /ip route   
    add gateway=10.10.9.1   distance=1 
	
  # DNS server, to allow ROS to resolve DNS queries from the CPUto fetch updates etc.  
  /ip dns 
    set allow-remote-requests=no   servers="10.10.9.1"

  #ES_GF-E_MT-hEX-S
  /interface ethernet  
    set comment="TP, VLAN-TRUNK TO CORE-SWITCH-SFP" 	        [find name=sfp1]
    set comment="UP, don't touch (temporarily)"                 [find name=ether1]
    set comment="UP, VLAN_USER1, OFFICE, UNMANAGED SWITCH"      [find name=ether2]
    set comment="UP, VLAN_USER2, KIDS"                          [find name=ether3]  
    set comment="UP, VLAN_USER2, LIVING ROOM"                   [find name=ether4]
	set comment="UP, don't touch (temporarily)"                 [find name=ether5]
	
## START OF SCRIPT
#creating variable per VLAN and other variables
  :global "VLAN_BKHOLE" { vlanid=001; name="VLAN_BKHOLE" ;  comment="BLACKHOLE"                                 }
  :global "VLAN_SYS"    { vlanid=008; name="VLAN_SYS"    ;  comment="SYS"                                       }
  :global "VLAN_GUEST"  { vlanid=011; name="VLAN_GUEST"  ;  comment="GUEST"                                     }
  :global "VLAN_USER1"  { vlanid=110; name="VLAN_USER1"  ;  comment="USER 1,all devices in one vlan"            }
  :global "VLAN_USER2"  { vlanid=120; name="VLAN_USER2"  ;  comment="USER 2,all devices in one vlan"            }
  :global filterUP "^UP, VLAN_USER"
  :global BridgeName  "bridge-VLANs"
  :global result


#show configuration before script execution  
  interface ethernet print where comment~"^UP."
  interface bridge port print

#clear anything in /interface bridge port if there is an existing configuration   
  :put "CLEAR EXISTING BRIDGE PORT CONFIGURATION 
  :foreach Interface in=[ /interface bridge port find ] do={ / 
    /interface bridge port remove $Interface
  }
  
#show that /interface bridge port is empty   
  /interface bridge port print

#add Bridge Ports according to the comment of the interfaces. PVID is set as well. 
  :put "STARTING SCRIPT-BASED BRIDGE PORT CREATION
  :foreach Interface in=[ /interface ethernet find where comment~$filterUP ] do={ / 
    :local readcomment ( [ :toarray ([/interface ethernet get $Interface]->"comment" ) ]->1 )
    :local readthisfield "vlanid"
    [:parse ("global \"$readcomment\";:global result (\$\"$readcomment\"->\"$readthisfield\")")]
    /interface bridge port add interface=$Interface bridge=$BridgeName pvid=$result 
    }
  
"show result
  :put "SHOW RESULTS"
  /interface ethernet print where comment~"^UP,"
  /interface bridge port print
  /interface bridge port print where pvid=110
  /interface bridge port print where pvid=120

and the reduced code generated OUTPUT:

/interface ethernet print where comment~"^UP,"
Flags: R - RUNNING; S - SLAVE
Columns: NAME, MTU, MAC-ADDRESS, ARP, SWITCH
#    NAME     MTU  MAC-ADDRESS        ARP      SWITCH 
;;; UP, don't touch (temporarily)
0 R  ether1  1500  08:55:31:0F:52:97  enabled  switch1
;;; UP, VLAN_USER1, OFFICE, UNMANAGED SWITCH
1  S ether2  1500  08:55:31:0F:52:98  enabled  switch1
;;; UP, VLAN_USER2, KIDS
2  S ether3  1500  08:55:31:0F:52:99  enabled  switch1
;;; UP, VLAN_USER2, LIVING ROOM
3  S ether4  1500  08:55:31:0F:52:9A  enabled  switch1
;;; UP, don't touch (temporarily)
4    ether5  1500  08:55:31:0F:52:9B  enabled  switch1

CLEAR EXISTING BRIDGE PORT CONFIGURATION 

STARTING SCRIPT-BASED BRIDGE PORT CREATION

SHOW RESULTS

/interface ethernet print where comment~"^UP,"
Flags: I - INACTIVE; H - HW-OFFLOAD
Columns: INTERFACE, BRIDGE, HW, PVID, PRIORITY, PATH-COST, INTERNAL-PATH-COST, HORIZON
#    INTERFACE  BRIDGE        HW   PVID  PRIORITY  PATH-COST  INTERNAL-PATH-COST  HORIZON
0 IH ether2     bridge-VLANs  yes   110  0x80             10                  10  none   
1 IH ether3     bridge-VLANs  yes   120  0x80             10                  10  none   
2 IH ether4     bridge-VLANs  yes   120  0x80             10                  10  none   

[admin@MikroTik] /interface/mesh>   /interface bridge port print where pvid=110
Flags: I - INACTIVE; H - HW-OFFLOAD
Columns: INTERFACE, BRIDGE, HW, PVID, PRIORITY, PATH-COST, INTERNAL-PATH-COST, HORIZON
#    INTERFACE  BRIDGE        HW   PVID  PRIORITY  PATH-COST  INTERNAL-PATH-COST  HORIZON
0 IH ether2     bridge-VLANs  yes   110  0x80             10                  10  none   

[admin@MikroTik] /interface/mesh>   /interface bridge port print where pvid=120
Flags: I - INACTIVE; H - HW-OFFLOAD
Columns: INTERFACE, BRIDGE, HW, PVID, PRIORITY, PATH-COST, INTERNAL-PATH-COST, HORIZON
#    INTERFACE  BRIDGE        HW   PVID  PRIORITY  PATH-COST  INTERNAL-PATH-COST  HORIZON
1 IH ether3     bridge-VLANs  yes   120  0x80             10                  10  none   
2 IH ether4     bridge-VLANs  yes   120  0x80             10                  10  none

but no clue why

:global result

and

    :local readcomment ( [ :toarray ([/interface ethernet get $Interface]->"comment" ) ]->1 )
    :local readthisfield "vlanid"

are working
but making

:global result

local

    :local readcomment ( [ :toarray ([/interface ethernet get $Interface]->"comment" ) ]->1 )
    :local readthisfield "vlanid"
    :local result

fails

Moreover, if first global

 [:parse ("global \"$readcomment\";:global result (\$\"$readcomment\"->\"$readthisfield\")")]

is changed to local

 [:parse ("lcoal \"$readcomment\";:global result (\$\"$readcomment\"->\"$readthisfield\")")]

I get

invalid value  for pvid, an integer required

means the comment was not read

Your question is like <<why if I change “3 + 3” to “3 x 3” I have different results?>>

If the variable is not global (or saved inside a file or mikrotik configuration), is not possible to see it inside another running script, as already I say, “:parse” is a separate execution of the script…
And as already wrotten, you must use “:global variable” to inform the separate script than you want to use that global variable.

Local variable exist only inside the “for” cycle, and when you call “:parse”, declared or not, is unable to see the variable because is not global.

I understand you want know how the scripts works, but if I say (and demostrate) than 3+3 do 6,
do not ask question aboput because 3x3 do 9… keep the things on how are working…

I think I almost have it, see below.
If anyone could answer the remaining question it would be just great :slight_smile:

[ :parse (" global "$readcomment"; :global result ( $"$readcomment" -> "$readthisfield" ) ") ]


  1. [ ... ] isolated the command? I'm still bit lost how to use (, [ or {. All I can say [ seems to be important when dealing with arrays
  2. :parse () anything what is in (" ... ") will converted in string than interpreted as command (parsing). $ are still used to link variable. In our case the variables readcomment and readthisfield
  3. global "$readcomment", declaring a variable named readcomment but as reference to the existing global variable readcomment. It is not a new variable existing within the scope of [ ... ]!
  4. Is this correct?
  5. It is somehow related to getting somehow access to the variable name (evl /globalname) as below?
[admin@MikroTik] > :global "vlan_common" {id=100;name="VLAN_COMMON";comment="COMMON SERVICES AND DEVICES / OFFICE"}
[admin@MikroTik] > :put ($"vlan_common")
comment=COMMON SERVICES AND DEVICES / OFFICE;id=100;name=VLAN_COMMON
[admin@MikroTik] > put [:parse ("global \"vlan_common\" ") ]
(evl /globalname=$vlan_common)
  1. ; :global result ( ... ), new command line indicated by ; and new command indicated by :. Whatever is the outcome of ( ... ) is assigned to result.
  2. $"$readcomment", content of readcomment is parsed into a variable name by prefixing $. As $ is escaped by \ it is parsed to link to a variable. Assume the content of readcomment is VLAN_USER1, it is parsed as $"VLAN_USER1", so it is linked to the global variable VLAN_USER1. " is used to put the variable name in quotes as required by the note in Operations with Arrays - Manual:Scripting - MikroTik Wiki.
  3. How is ensured that it looks for global variable as I readcomment is referenced again but not this one?
  4. Is it due to :global?
  5. "$readthisfield", almost the same as above but here, only the array key name is determined what is stored in readthisfield.
  6. -> is the command to get an array element by key, in our case it would than store the content of the array element stored in "$readthisfield" in the array $"VLAN_USER1" and store it in $result
  1. If you use put, you obtain the internal equivalent script, if you omit :put, the :parse script is executed
[rex@net] > :put [:parse ":put 777"]  
(eval /putmessage=777)
[rex@net] > [:parse ":put 777"]      
777
  1. Parse first convert the string in script, for example:
    if readfromthis is “vlan_common” and readthisfield is “name”
    this
    [:parse (“global "$readfromthis";:global result ($"$readfromthis" → "$readthisfield")”)]
    is internally converted first to:
    [:parse (“global "vlan_common";:global result ($"vlan_common" → "name")”)]
    replacing $readfromthis with “vlan_common” and $readthisfield with “name”
    then is converted to internal script language, but for example is converted to:
    :global “vlan_common”;:global result ($“vlan_common”->“name”)

3), 5) and 6) understanding 2) and what are a global variable, the 3), 5 and 6) are useless

  1. where is the question?

  2. Not, this indicate → what element you want read or write based on what the script do.
    For example on array {id=100;name=“VLAN_COMMON”;comment=“COMMON SERVICES AND DEVICES / OFFICE”}

Read:
:put ($array->“name”)
VLAN_COMMON

Write:
:set ($array->“name”) “newname”
the array change to {id=100;name=“newname”;comment=“COMMON SERVICES AND DEVICES / OFFICE”}

Try This

:local id 1;
:local nwshost Tes1,Tes2,Tes3,Tes4,Tes5,Tes6,Tes7,Tes8,Tes9

:foreach nwsarr in=$nwshost do={
:local date [/system clock get date]
:local time [/system clock get time]

[:parse ":global Down$nwsarr"];
[:parse ":global check$nwsarr"];
[
   :execute (" \
     :if (\$\"check$nwsarr\" != 1) do={ :set check$nwsarr \"2\"; }; \; \
     :set Down$nwsarr \"$date $time\";  \
   ")

];

:set id ($id+1)
}

Just necroposting. Nothing usable and full of frills and errors. For example: :if (check$nwsarr must be :if ($check$nwsarr
/system clock
:local datetime “$[get date] $[get time]”
:local nwshost (“Tes1”,“Tes2”,“Tes3”,“Tes4”,“Tes5”,“Tes6”,“Tes7”,“Tes8”,“Tes9”)
:local id (1 + [:len $nwshost])

:foreach item in=$nwshost do={
[:parse “:global check$item ; :if ($check$item != 1) do={:set check$item 2}”]
[:parse “:global Down$item "$datetime"”]
}

If in execute
:if ($"check$nwsarr" != 1) do={ :set check$nwsarr "2"; }; \

While it’s nifty trick to use [:parse] to create new globals or use one without a declaration… In most cases, your better off using an array-of-maps or array-of-lists & using TWO :foreach loops.

And bad examples of using [:parse] to have “dynamic variable names” are even less helpful.

You edit your post, but is still full of frills and errors.

If you try to correct someone who corrects you, at least make sure you correct well...
If 2 is a number there is no need for "", if instead it is a string, then consistently the 1 must also be quoted...
And about "check$nwsarr" must be, for the same reason, :set "check$nwsarr" and :set "Down$nwsarr"
I do not have added " (on all places) for not add unreadability on code.
Since someone is not so dumb to use problematic variable names... Or not?


Rightly so, what @Amm0 says is correct.
Without parse, execute and other frills...
{

:global gev
:if ([:typeof [$gev]] = "nil") do={:global gev [:toarray ""]}

/system clock
:local datetime "$[get date] $[get time]"
:local nwshost ("Tes1","Tes2","Tes3","Tes4","Tes5","Tes6","Tes7","Tes8","Tes9")
:local id (1 + [:len $nwshost])

added for test

:set ($gev->"checkTes2") 1 ; # for test if work (($gev->"checkTes2") != 1)

:foreach item in=$nwshost do={
:if (($gev->"check$item") != 1) do={:set ($gev->"check$item") 2}
:set ($gev->"Down$item") $datetime
}

added for test

:put ($gev->"checkTes2")
:put ($gev->"DownTes2")
:put ($gev->"checkTes4")
:put ($gev->"DownTes4")

}