Frustrated trying to create a script

Can someone please tell me what I’m doing wrong (or all the things I’m doing wrong):

/system
:local cdate [clock get date] 
:local yyyy  [:pick $cdate 0  4]
:local MM    [:pick $cdate 5  7]
:local dd    [:pick $cdate 8 10]
:local identitydate "$[identity get name]_$yyyy-$MM-$dd"

/file print file=fileup 

#/file remove fileup.txt

:local fileup "$[$identitydate].txt-dynamicdata"

/ip/cloud print file=cloud
/ip/dhcp-server/lease print file=leases
/interface/bridge/host print file=hosts
/interface/wireless/registration-table print file=registrations

:local cloudcontents [/file get $cloud contents]
:local leasescontents [/file get $leases contents]
:local hostscontents [/file get $hosts contents]
:local registrationscontents [/file get $registrations contents]

:local fileupcontents "$[$cloudcontents] . "\r\n" . $[$leasescontents] . "\r\n" . $[$hostscontents] . "\r\n" . $[$registrationscontents]"

/file print $fileup contents = $[$fileupcontents]

#/tool fetch upload=yes mode=ftp ascii=no src-path="/fileup.txt" dst-path="/mikrotik-backups/fileup.txt" address=192.168.2.22 port=21 user=mikrotik password=mikrotik

You’re incorrectly using “string interpolation” is your main issue…

:local fileup "$[$identitydate].txt-dynamicdata"

with $identitydate being a string variable, so should be following:

:local fileup "$($identitydate).txt-dynamicdata"

or

:local fileup "$identitydate.txt-dynamicdata"

should work.
… and mistake is repeated throughout.

Essentially DO NOT USE square brackets to access a variable! Square bracket run commands/functions and what returned is put into the string. String and numbers in variable are not functions/commands, so they don’t “return” anything – only store. So all the place you’re putting a varable into a string use “my string is $mystr”.

Perhaps an example help:


{
:local myvar Mikrotik
:local “my-var-kabab” Mikrotik

:put “a command’s return value using brackets: $[/system identity get name]”

:put " vs"

:put “using a simple variable in string is just a dollar sign and var name, so: $myvar”
:put " BUT for more complex names or part of array or variables ‘touching’, you need use a ‘grouping’ $(…), here: $($“my-var-kabab”)"

:put “* as shown, use CAN use unescaped quotes INSIDE the grouping syntax $(parenthesis) "
:put " note above use the dollar sign needs to be escaped to be printed $”
:put " along with backsplash that escape the dollar too, so \$ if we wanted the backsplash in output"

:global myarray {“mykey”:“ex”; “mystr”=“sometext”}
:put “now if the variable your including in string is an ‘array’ type… it also uses the ‘dollar grouping’ with parenthesis syntax”

:put “so to get the mystr value inside myarray that just $($myarray->“mystr”)”

:put “* IMPORARTANTLY array types SHOULD NOT be used DIRECTLY in a string”
:put " while $myarray in string is allowed, and may work, just complex logic gets involved always using ‘$[:tostr $myarray]’ avoid this"
:put " so to :put an ENTIRE array, you start with the :tostr COMMAND, like: $[:tostr $myarray] "
:put " note the :tostr is a command that RETURNS, so why it uses square brackets"
}

a command’s return value using brackets: bigdude
vs
using a simple variable in string is just a dollar sign and var name, so: Mikrotik
BUT for more complex names or part of array or variables ‘touching’, you need use a ‘grouping’ $(…), here: Mikrotik

  • as shown, use CAN use unescaped quotes INSIDE the grouping syntax $(parenthesis)
    note above use the dollar sign needs to be escaped to be printed $
    along with backsplash that escape the dollar too, so $ if we wanted the backsplash in output
    now if the variable your including in string is an ‘array’ type… it also uses the ‘dollar grouping’ with parenthesis syntax
    so to get the mystr value inside myarray that just sometext
  • IMPORARTANTLY array types SHOULD NOT be used DIRECTLY in a string
    while $myarray in string is allowed, and may work, just complex logic gets involved always using ‘$[:tostr ex]’ avoid this
    so to :put an ENTIRE array, you start with the :tostr COMMAND, like: ex;mystr=sometext
    note the :tostr is a command that RETURNS, so why it uses square brackets

Also, I would warn against trying to do too much in a single expression. At some point it just won’t work and there are no ways to debug it.
It is safest to move some calculated value into a variable first before using it inside another expression, and also best to use the . string concat operator instead of having variable substitutions inside quoted strings.
So use the method you have used to set “fileupcontents”, not the method you used to set “fileup”.
(aside from having instead of () )

Fair point about “dot concatenation”. I do use both myself. More :local var in above code would also clean it up and make more “debuggable” with the intermediate values.


But it is a “pick your poison” situation — there be dragons with arrays and string – wherever you turn.
e.g.

:global myarraymap {"key1"="text1";"key2"="text2"}
:global myarraylist ("str1","str2")
:global mystr "myglobalvar"
:put ($myarraylist.$mystr) 
        # output:     "str1myglobalvar;str2myglobalvar"
:put ($myarraymap.$mystr) 
        # outputs empty newline

FWIW my “frustration in trying to create a script” is more pedantic: i want ‘.’ to work with functions, so it acts like a map() to modify the array, that really cleanup some scripts. But ain’t putting much hope in that.

Just for example of how “dot concatenation” works with array and functions today, and also how square brackets change things…:

:global myarraylist ("text1","text2")
:global myfunction do={:return "something $1 $0"}      
:put ($myarraylist.$myfunction)      
# text1;text1(code);text2;text2(code)                               
:put ($myarraylist.[$myfunction])
# text1something  $myfunction;text2something  $myfunction

Thank you very much.

I have a much better understand of when to use brackets now.

But, I still can’t get this script to work:

/system
:local cdate [clock get date] 
:local yyyy  [:pick $cdate 0  4]
:local MM    [:pick $cdate 5  7]
:local dd    [:pick $cdate 8 10]
:local identitydate "$[identity get name]_$yyyy-$MM-$dd"

:local fileup "$identitydate-dynamicdata"

/ip/cloud print file=cloud
/ip/dhcp-server/lease print file=leases
/interface/bridge/host print file=hosts
/interface/wireless/registration-table print file=registrations

:local cloudcontents [/file get cloud.txt contents]
:local leasescontents [/file get leases.txt contents]
:local hostscontents [/file get hosts.txt contents]
:local registrationscontents [/file get registrations.txt contents]

:local fileupcontents "$cloudcontents . $leasescontents . $hostscontents . $registrationscontents"

/file print file="$fileup" 
/file set $fileup contents=$fileupcontents

/tool fetch upload=yes mode=ftp ascii=no src-path="/$fileup" dst-path="/mikrotik-backups/$fileup" address=192.168.2.22 port=21 user=mikrotik password=mikrotik

/file remove [find name=$fileup]

/file remove "$fileup"

The first error is not specify the RouterOS version.
The second, if one file is > 4KB, nothing is returned.
The third, if the fileupcontents size is > 4KB, the file is not written.

RouterOS 7.12beta3

I don’t think any of the initial files are greater than 4KB. But, I don’t understand what “nothing is returned” means.

Also if on device name is present one forbidden characters on remote filesystem, the file is not created.


Is easy that you go over 4K…

/system
:local identitydate “$[identity get name]_$[clock get date]”

/file remove [find where name=tmpresults.txt]
:delay 1s
:execute “/ip cloud pri; :put "\r\n"; /ip dhcp-server lease pri det; :put "\r\n"; /int bridge host pri det; :put "\r\n"; /int wireless reg pri det” file=tmpresults.txt
:delay 2s

/tool fetch upload=yes mode=ftp ascii=no src-path=tmpresults.txt dst-path=“/mikrotik-backups/$identitydate-dynamicdata” address=192.168.2.22 port=21 user=mikrotik password=mikrotik

/file remove [find where name=tmpresults.txt]

Wow!

That is really beautiful code.

I am hoping to deploy this script across multiple devices, including hex, rb5009, hAPac and hAPax.

So, the wifiwave2 devices I inserted:

/int wireless reg pri det

to look like this:

:execute "/ip cloud pri; :put \"\\r\\n\";  /ip dhcp-server lease pri det; :put \"\\r\\n\"; /int bridge host pri det; :put \"\\r\\n\"; /int wireless reg pri det; :put \"\\r\\n\";/int wif reg pri det" file=tmpresults.txt

The code above is executed until the part where it results in a ‘command not found’ error.

For example, on an ax3 (wifiwave2), the code stops at:

/int wireless reg pri det

On a hEX, it stops, but I’m not missing any data because the hEX has neither /int wireless nor /int wifiwave2

Is there a way to get it ignore the error?

Thank you very much.

You must adapt the script for detect if on the device where is executed is installed (or not) the wifiwave2 package…
:local identitydate “$[/sys identity get name]_$[clock get date]”
:local stringexec “/sys identity print; :put "\r\n"; /ip cloud pri; :put "\r\n"; /ip dhcp-server lease pri det; :put "\r\n"; /int bridge host pri det”

:if ([:len [/int find where ( (type=“wlan”) or (type=“wifi”) )]] > 0) do={
:if ([:len [/sys package find where name=“wifiwave2”]] = 1) do={
:set stringexec “$stringexec; :put "\r\n"; /int wifiwave2 reg pri det”
} else={
:set stringexec “$stringexec; :put "\r\n"; /int wireless reg pri det”
}
}

/file remove [find where name=tmpresults.txt]
:delay 1s
:execute $stringexec file=tmpresults.txt
:delay 2s

/tool fetch upload=yes mode=ftp ascii=no address=192.168.2.22 port=21 user=mikrotik password=mikrotik
src-path=tmpresults.txt dst-path=“/mikrotik-backups/$identitydate-dynamicdata.txt”

/file remove [find where name=tmpresults.txt]

Truly brilliant.

And very kind of you to do this.

Thank you.

Now to figure out how to make all this data useful…

I made a couple of small changes (that actually work, but I’m sure there’s a more efficient way):

Added another IF for devices with no wireless interfaces
Added .txt so Windows isn’t confused
Added system identity to beginning of saved file (to avoid having to look at the file name to know which device’s data I’m looking at)

/system
:local identitydate "$[identity get name]_$[clock get date]"
:local stringexec   "/system iden print; :put \"\\r\\n\"; /ip cloud pri; :put \"\\r\\n\";  /ip dhcp-server lease pri det; :put \"\\r\\n\"; /int bridge host pri det"

:if ([:len [/system package find where name="wifiwave2"]] > 1) do={
    :set stringexec "$stringexec; :put \"\\r\\n\" /int wifiwave2 reg pri det"
} 

:if ([:len [/system package find where name="wifiwave2"]] > 1) do={
    :set stringexec "$stringexec; :put \"\\r\\n\" /int wireless reg pri det"
}


/file remove [find where name=tmpresults.txt]
:delay 1s
:execute $stringexec file=tmpresults.txt
:delay 2s

/tool fetch upload=yes mode=ftp ascii=no address=192.168.2.22 port=21 user=mikrotik password=mikrotik \
    src-path=tmpresults.txt dst-path="/mikrotik-backups/$identitydate-dynamicdata.txt"

/file remove [find where name=tmpresults.txt]

the double identical “if” instead of “if/else” make no sense…

Altered the previous post for reflect the changes you want.

I actually tried to make it work with if-else, but failed.

Then I thought that maybe there might be a possibility of both packages being installed, in which case the repeated if would be successful.

Regardless, thank you again.

Now I’m playing with the get command to try to do this in a way that produces better formatted results (for importing into something easy to view).

If only The Dude provided this info, I’d be set.