Community discussions

 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Functions in CMD Scripts

Wed Jan 27, 2010 12:55 am

Here is a script that has been useful to me, and hope others will find it useful as well.
I've been using Mikrotik for a few years now and found myself using the same code multiple times.
Mikrotik CMD scripting doesn't support functions you can call from your scripts, so I decided to try and come up with a fairly easy way to implement this.

I'm posting here to get any comments / suggestions / feedback on this code. If all the kinks are worked out, hopefully others will be able to contribute their functions as well.

Note: Lua can handle functions within scripts, but isn't implemented as of RouterOS v2.x, v3.30, or v4.5. Also you could use multiple scripts passing global variables to/from them, but this requires that no other script use that same global variable. By "injecting" global variables into a function, this should make the functions more flexible and minimize global variable collisions.

Here it is:
# Script name: Functions
# Functions script library
# Remember:  ' $ ' (dollar sign), ' " ' (double-quote), and ' \ ' (backslash) must be escaped with preceding backslash '\' to when used in functions
# Function input array is passed from calling script using:
#	string:  		  ":local input \"<input1,input2,...>\"; "
#	command output: ":local input [/path/to/command get <item property>];"
# Function output array is stored in function's local $output variable
# Functions should always end with ; (semi-colon)   - this makes it easier when calling from another script
#
# Ex. To call MyFunc with input "1,2,3,4", storing function's output in global var MyOutput
#        From calling script:
# Code
#    /system script run "Functions"
#    :global MyFunc
#
#    :global MyOutput ""
#    :local runFunc [:parse (":global MyOutput;" . \
#			    ":local input \"1,2,3,4\";" . \
#		                 $MyFunc . \
#			    ":set MyOutput \$output")
#		   ]
#    $runFunc
# End Code
#
# The global variable $MyOutput now contains an array of output values from function
# To display output:
#    :put [:pick $MyOutput 0]
#    :put [:pick $MyOutput 1]
#    :put [:pick $MyOutput 2]
#    :put [:pick $MyOutput ...]


# Functions
#------------

# RandomNumGen: Generates a fairly random number from date, time, mem-free, and cpu-load
# Input array: none
# Output array:
#	0 = number generated
:global RandomNumGen ":local output \"\"
			:set output (\$output . [:pick [/system clock get date] 7 11])
			:set output (\$output . [:pick [/system clock get date] 4 6])
			:set output (\$output . [:pick [/system clock get time] 0 2])
			:set output (\$output . [:pick [/system clock get time] 3 5])
			:set output (\$output . [:pick [/system clock get time] 6 8])
			:set output (\$output . [/system resource get free-memory])
			:set output (\$output . [/system resource get cpu-load])
			:set output [:tonum \$output]
			:set output [:toarray \$output];"

# DateToNum: Converts a given date (mmm/dd/yyyy) to numeric date (yyyymmdd)
# Input array:
#	0 = string date "mmm/dd/yyyy"
# Output array:
#	0 = numeric date yyyymmdd
:global DateToNum "	:local output \"\"
			:set input [:toarray \$input]
			:if ([:len \$input] > 0) do={
				:local input1 [:tostr [:pick \$input 0]]
				:local months [:toarray \"jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec\"]
				:local mon 0
				:for x from=0 to=([:len \$months] - 1) do={
				   :if ([:tostr [:pick \$months \$x]] = [:tostr [:pick \$input1 0 3]]) do={
				      :if (\$x < 9) do={ :set mon (\"0\" . (\$x + 1)) } else={ :set mon (\$x + 1) } } }
				:set output ([:pick \$input1 7 11] . \$mon . [:pick \$input1 4 6])
				:set output [:tonum \$output]
				:set output [:toarray \$output]
			};"

# TimeToNum: Removes ':' from given time (hh:mm:ss) -> (hhmmss)
# Input array:
#	0 = string time "hh:mm:ss"
# Output array:
#	0 = numeric time hhmmss
:global TimeToNum "	:local output \"\"
			:set input [:toarray \$input]
			:if ([:len \$input] > 0) do={
				:local input1 [:tostr [:pick \$input 0]]
				:for x from=0 to=([:len \$input1] - 1) do={
				   :if ([:tostr [:pick \$input1 \$x (\$x + 1)]] != \":\") do={
				      :set output (\$output . [:tostr [:pick \$input1 \$x (\$x + 1)]]) } }
				:set output [:tonum \$output]
				:set output [:toarray \$output]
			};"

# RegExFind: Searches a given string for an exact regex pattern
# Input array:
#	0 = string regex search
#	1 = string regex pattern
# Output array:
#	0 = number start index of regex match (-1 if not found)
#	1 = number end index of regex match (-1 if not found)
:global RegExFind "	:local output \"\"
			:set input [:toarray \$input]
			:if ([:len \$input] > 1) do={
				:local input1 [:tostr [:pick \$input 0]]
				:local input2 [:tostr [:pick \$input 1]]
				:for x from=0 to=([:len \$input1] - 1) do={
				   :if ([:pick \$input1 \$x [:len \$input1]]  ~ (\"^\" . \$input2)) do={
				      :for y from=\$x to=([:len \$input1] - 1) do={
				         :if ([:pick \$input1 \$x (\$y + 1)] ~ (\$input2 . \"\\\$\")) do={
				            :set output (\$x . \",\" . \$y) } } } }
				:if ([:len \$output] = 0) do={ :set output \"-1,-1\" }
				:set output [:toarray \$output]
			};"

# -----------------
# End Functions
Demo script to show how functions can be used"
# Script name: DemoFunctions
# Demo of function library

# Import Functions script and functions we are using
/system script run "Functions"
:global RandomNumGen
:global DateToNum
:global TimeToNum

# Demo: RandomNumGen
:global DemoRandomNum 0
:local runFunc 	[:parse	(":global DemoRandomNum;" . \
			 $RandomNumGen . \
			 ":set DemoRandomNum \$output")
		]
$runFunc
:put ("Random number: " . [:pick $DemoRandomNum 0])

# Demo: DateToNum
:global DemoDate 0
:local runFunc 	[:parse	(":global DemoDate;" . \
			 ":local input [/system clock get date]; " . \
			 $DateToNum . \
			 ":set DemoDate \$output")
		]
$runFunc
:put ("Date to number: " . [:pick $DemoDate 0])

# Demo: TimeToNum
:global DemoTime 0
:local runFunc 	[:parse	(":global DemoTime;" . \
			 ":local input [/system clock get time]; " . \
			 $TimeToNum . \
			 ":set DemoTime \$output")
		]
$runFunc
:put ("Time to number: " . [:pick $DemoTime 0])
Again, please post feedback with any suggestions / fa
 
User avatar
normis
MikroTik Support
MikroTik Support
Posts: 24280
Joined: Fri May 28, 2004 11:04 am
Location: Riga, Latvia

Re: Functions in CMD Scripts

Wed Jan 27, 2010 2:28 pm

wow, thanks a lot! If all works fine with it, Lua should return in v5

if you add this to the Wiki, I will give you a RouterOS license for your effort.
No answer to your question? How to write posts
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Wed Jan 27, 2010 11:26 pm

 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Tue Feb 02, 2010 10:14 pm

Here is an example of a recursive function.

This will store and list all possible three character combination of numbers 1,2,3.
:local runFunc

:global output ""
:global recurseFunc (":global recurseFunc;
		      :global output;
		      :local runFunc;
		      :if ([:len \$base] < 3) do={
		         :foreach c in=[:toarray \"1,2,3\"] do={
		            :set output (\$output . \",\" . \$base . [:tostr \$c])
		            :set runFunc (\":local base \\\"\" . \$base . [:tostr \$c] . \"\\\"; \" . \$recurseFunc)
		            :set runFunc [:parse \$runFunc]
		            :put \$runFunc
		         }
		      }; ")

:set runFunc (":local base \"\"; " . $recurseFunc)
# To see how the function will be interperted, uncomment next line
#:put $runFunc
:set runFunc [:parse $runFunc]
:put $runFunc
:foreach char in=[:toarray $output] do={
   :put [:tostr $char]
}

The output:
1
11
111
112
113
12
121
122
123
13
131
132
133
2
21
211
212
213
22
221
222
223
23
231
232
233
3
31
311
312
313
32
321
322
323
33
331
332
333
This is included in the wiki as well (see above post).
 
bluestar
newbie
Posts: 31
Joined: Fri May 06, 2005 1:21 am

Re: Functions in CMD Scripts

Fri Feb 05, 2010 1:14 pm

Can someone post MD5 calculating functions, please ?

I need mikrotik script to calculate md5 values od some parameters to be used in other scripts.
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Mon Feb 08, 2010 6:28 pm

Can someone post MD5 calculating functions, please ?

I need mikrotik script to calculate md5 values od some parameters to be used in other scripts.
What parameters do you need an md5 function for? What other scripts need an md5 hash as input?
 
bluestar
newbie
Posts: 31
Joined: Fri May 06, 2005 1:21 am

Re: Functions in CMD Scripts

Sat Feb 13, 2010 12:14 pm

All our logins are dinamicly changed at random itervals. We have radious server corectly configured in production. Our clients Mikrotik Routers which use L2TP tunnels authentification to central office need also to have automaticly and periodicly changes in password for authentification. For that function to work we need md5 to be able to grenerate hash for next parts of scripts.
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Tue Feb 16, 2010 11:17 pm

I won't be able to program an MD5 function using CMD scripting, it's just too complex and time consuming at the moment.
Passwords on Mikrotik devices are not set using a hashed algorithm. You can set them directly without hasing.
I believe Lua will support MD5 hashing in RouterOS v5, stay tuned for that.

If you really need to hash a password using MD5, I'd suggest running the below code from a Linux box:
ssh <user>@<router> ':put [/ppp secret get <user> password]' | md5sum
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Wed May 19, 2010 3:06 am

I've developed an MD5 hashing algorithm for Mikrotik RouterOS script. So far, it works with any data that can be read within RouterOS (file, variable, [get] properties, etc...). It works great and is accurate, however I'm still working on optimizing it a bit, and cleaning the code for production use.

If you are still interested, I can release this code for a minimal fee (as it is very time consuming to implement) via a PayPal payment to: dscomputer_2000[at]hotmail[dot]com

Also, feel free to contact me via the above email address to further work out details.

Thank You,
Doug
Doug
 
fewi
Forum Guru
Forum Guru
Posts: 7734
Joined: Tue Aug 11, 2009 3:19 am

Re: Functions in CMD Scripts

Wed May 19, 2010 7:04 am

I've developed an MD5 hashing algorithm for Mikrotik RouterOS script
You are absolutely nuts, and I mean that in the best possible way. MD5 in the built
in scripting language is one hell of a hack.
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Wed May 19, 2010 7:48 am

fewi,
Yeah it was a real challenge. I've been involved in some projects recently, needed MD5 hashing function (with no external dependencies), and realized there was also a need arise on this forum. It uses this post's method of creating functions with RouterOS scripting. It also works on large files (ex. an 8KB file split into 2x4KB files read using [/file get xxx contents]) will generate the same hash as a Linux md5sum function on the single 8KB file.

Also, to anyone looking for advanced scripting, I am available (at my email listed above).
Doug
 
markmcn
Frequent Visitor
Frequent Visitor
Posts: 78
Joined: Wed Mar 03, 2010 2:15 am

Re: Functions in CMD Scripts

Mon Jul 26, 2010 6:46 pm

dssmiktik i'm just wondering how much are you selling copies of that md5 calc script for?
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Fri Jul 30, 2010 12:42 am

Details on how to obtain the script can be found here
Doug
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Mon Aug 02, 2010 9:51 pm

Also, feel free to e-mail me with any questions at: dscomputer_2000[at]hotmail[dot]com
Doug
 
dssmiktik
Forum Veteran
Forum Veteran
Topic Author
Posts: 732
Joined: Fri Aug 17, 2007 8:42 am

Re: Functions in CMD Scripts

Tue Oct 05, 2010 7:49 am

markmcn & bluestar,

Let me know if you're still interested in the MD5 script, or how I may provide assistance.

Best regards,
Doug
 
thegink
just joined
Posts: 3
Joined: Wed Oct 12, 2011 5:02 am

Re: Functions in CMD Scripts

Wed Oct 12, 2011 5:22 am

Here's a function I've been using which accepts input from the user.
It's an extension of another of dssmiktik's scripts, found here
# Script name: Functions
# Functions script library
# Remember:  ' $ ' (dollar sign), ' " ' (double-quote), and ' \ ' (backslash) must be escaped with preceding backslash '\' to when used in functions
# Function input array is passed from calling script using:
#   string:          ":local input \"<input1,input2,...>\"; "
#   command output: ":local input [/path/to/command get <item property>];"
# Function output array is stored in function's local $output variable
# Functions should always end with ; (semi-colon)   - this makes it easier when calling from another script
#
# Ex. To call MyFunc with input "1,2,3,4", storing function's output in global var MyOutput
#        From calling script:
# Code
#    /system script run "Functions"
#    :global MyFunc
#
#    :global MyOutput ""
#    :local runFunc [:parse (":global MyOutput;" . \
#             ":local input \"1,2,3,4\";" . \
#                       $MyFunc . \
#             ":set MyOutput \$output")
#         ]
#    $runFunc
# End Code
#
# The global variable $MyOutput now contains an array of output values from function
# To display output:
#    :put [:pick $MyOutput 0]
#    :put [:pick $MyOutput 1]
#    :put [:pick $MyOutput 2]
#    :put [:pick $MyOutput ...]


# Functions
#------------


# Prompt: Puts a prompt on the command line, then accepts an input from the user.
# Input array:
#   0 = prompt to display
#   1 = echo typed input (0 - default) or hide (1)
# Output array:
#   0 = input from user
:global Prompt ":local output \"\"
            :set input [:toarray \$input]
            :if ([:len \$input] > 0) do={
                :local input1 [:tostr [:pick \$input 0]]
                :local echo 0
                :if ([:len \$input] > 1) do={ 
                    :set echo [:tonum [:pick \$input 1]]
                }
                :local asciichar (\"0,1,2,3,4,5,6,7,8,9,\" . \
                                  \"a,b,c,d,e,f,g,h,i,j,k,l,\" . \
                                  \"m,n,o,p,q,r,s,t,u,v,w,x,y,z,\" . \
                                  \"A,B,C,D,E,F,G,H,I,J,K,L,\" . \
                                  \"M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,\" . \
                                  \".,/\")
                :local asciival {48;49;50;51;52;53;54;55;56;57; \
                                 97;98;99;100;101;102;103;104;105;106;107;108; \
                                 109;110;111;112;113;114;115;116;117;118;119;120;121;122; \
                                 65;66;67;68;69;70;71;72;73;74;75;76; \
                                 77;78;79;80;81;82;83;84;85;86;87;88;89;90; \
                                 46;47}
                
                :local findindex;
                :local loop 1;
                :local key 0;
                :put \"\$input1\";
                :while (\$loop = 1) do={
                
                    :set key ([:terminal inkey])
                    
                    :if (\$key = 8) do={
                        :set output [pick \$output 0 ([:len \$output] -1)];
                        :if (\$echo != 1) do={
                            :put \"\r\$output              \";
                            /terminal cuu 0;
                        } else={
                            :local stars;
                            :if ([:len \$output] > 0) do={
                                :for x from=0 to=([:len \$output]-1) do={
                                    :set stars (\$stars . \"*\");
                                }    
                            }
                            :put \"\r\$stars                         \";
                            /terminal cuu 0;
                        }
                    }
                    
                    :if (\$key = 13) do={
                        :set loop 0;
                        put \"\";
                        } else={
                           
#                       # Convert numeric ascii value to ascii character
                        :set findindex [:find [:toarray \$asciival] \$key]
                        :if ([:len \$findindex] > 0) do={
                            :set key [:pick [:toarray \$asciichar] \$findindex]
                            :set output (\$output . \$key);
                            :if (\$echo != 1) do={
                                :put \"\r\$output                \";
                                /terminal cuu 0;
                            } else={
                                :local stars;
                                :for x from=0 to=([:len \$output]-1) do={
                                    :set stars (\$stars . \"*\");
                                    }
                                                            
                                :put \"\r\$stars                     \";
                                /terminal cuu 0;
                            }
                        }
                    }
                }
                :set output [:toarray \$output]
            };"
                


# -----------------
# End Functions
Here are some examples of its use:
:global Prompt;
:local runFunc;
/system script run Functions

# Prompt for IP address
    :local ipaddress;
    :set runFunc [:parse (":global ipaddress;" . \
         ":local input \"Enter IP address or leave blank to skip:\";" . \
                   $Prompt . \
         ":set ipaddress \$output")
     ]
    
:do { $runFunc } while=([:len [:toip value=$ipaddress]] = 0 && [:len $ipaddress] > 0);

# Prompt for password - mask characters typed
    :local password;
    :set runFunc [:parse (":global password;" . \
             ":local input \"Enter System Password or leave blank to skip:,1\";" . \
                       $Prompt . \
             ":set password \$output")
         ]
        
    $runFunc;
    
    if ([:len $password] > 0) do={ /user set admin password=$password }

Thanks for your work, Doug!
t
 
samuel345
just joined
Posts: 1
Joined: Tue Jan 08, 2013 10:37 am

Re: Functions in CMD Scripts

Tue Jan 08, 2013 10:38 am

I was trying to get this script done for the last couple of weeks, but was always getting errors. Thanks for putting up this post. It has helped me troubleshoot my script error.
__________________
Printer Issues
 
HaPe
Member Candidate
Member Candidate
Posts: 232
Joined: Fri Feb 10, 2012 10:24 pm
Location: Poland

Equivalent of read in bash

Fri May 24, 2013 8:16 pm

Hi,
Is there an equivalent of read function (for example from bash) in mikrotik scripting?
In anticipation on new The Dude release.
 
JAza
just joined
Posts: 24
Joined: Sun Jun 10, 2012 1:07 am

Re: Functions in CMD Scripts

Fri Jul 10, 2015 2:40 am

Is this still relevant with regards to ROS v6 or even v5? Or is there a more 'native' way to pass parameters and call functions/routines or even other scripts now?

thx.
 
User avatar
mrz
MikroTik Support
MikroTik Support
Posts: 5944
Joined: Wed Feb 07, 2007 12:45 pm
Location: Latvia
Contact:

Re: Functions in CMD Scripts

Fri Jul 10, 2015 10:53 am

 
jeroenp
Member Candidate
Member Candidate
Posts: 159
Joined: Mon Mar 17, 2014 11:30 am
Location: Amsterdam
Contact:

Re: Functions in CMD Scripts

Thu May 05, 2016 8:29 am

With the (now not so new) function syntax any more: anybody having time to update the examples using the function syntax http://wiki.mikrotik.com/wiki/Manual:Sc ... #Functions

(note to self: a function does not need to be :global as it can be :local just fine)
 
User avatar
bajanetworks
just joined
Posts: 4
Joined: Wed Jun 20, 2018 9:51 pm
Location: Baja California, México
Contact:

Re: Functions in CMD Scripts

Tue Sep 18, 2018 10:40 am

Hello guys,
I am using the DateToNum and TimeToNum Functions (included in the Functions Library) to build unique filenames and log simple queues data every second.
Then I have a php script pulling those files to save their data to a MySQL DB. I do remove those files one by one using the API.
Everything works pretty good until midnight when the "00" are automatically removed and not appended to the the string for the filename so I ended up with file names like 20180917-325 instead of 20180917-000325.
Does someone know how to fix this?

Thank you guys
LCC José Luna
Director de Tecnología
Cel. +52 646 131 6198
http://bajanetworks.com.mx

Who is online

Users browsing this forum: No registered users and 17 guests