Community discussions

MikroTik App
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Isolate a value from a console output

Wed Mar 20, 2024 4:10 am

I wanted to capture the WAN address of my HAP AX3 router.
I can see the address by issuing the print command.
[@MikroTik] > ip address/print proplist=address where interface=ether1_WAN
Flags: D - DYNAMIC
Columns: ADDRESS
#   ADDRESS
2 D 1.2.3.4/21

Now, I wanted to isolate the address, something like "grep -o".
Is there a way to print a single value from a console output?
Is there a regex function?
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Wed Mar 20, 2024 5:21 am

If you want to store it a variable it looks like:

:global wanIP [/ip/address get [find interface=ether1_WAN] address]

You can then use that variable in future command. To output it, it's just:
:put $wanIP

Or, in a string like
:put "my WAN IP is $wanIP"

Alternatively, if you using DDNS in IP > Cloud, you can also get the detected WAN address via
:put [/ip cloud get public-address]
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output  [SOLVED]

Wed Mar 20, 2024 5:25 am

Is there a regex function?
In the "find", "print where", or in an :if like statement you can use the tilde to match a variable or attribute against a regex:
attribute~".*"
Only boolean matches and no grouping or replacement however.

So in the find shown above, you could search for anything that starts with "ether1"
:global wanIP [/ip/address get [find interface~"^ether1"] address]
(although there is also default-name= on /interface to match on too)
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:10 pm

I appreciate your help @Amm0.

If I use
:do {:set currentIPv4 ([/ip address/get [find interface=$wanInt] address])}
put $currentIPv4
1.2.3.4/21
The resulting address contains the network mask.
Now, I need to remove this mask number, but how?
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12014
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:25 pm

where you get the useless do {} and the other frills??????????
{
/ip address
:local wfaddress [get [find where interface=$wanInt] address]
:put $wfaddress
:local waddress  [:pick $wfaddress 0 [:find $wfaddress "/" -1]]
:put $waddress
}
Last edited by rextended on Fri Mar 22, 2024 4:33 pm, edited 2 times in total.
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:28 pm

Nevermind...
I think this works
:do {:set currentIPv4 ([/ip address/get [find interface=$wanInt] address]); :set currentIPv4 [:pick $currentIPv4 0 {[:len $currentIPv4] - 3}]}
put $currentIPv4
1.2.3.4
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12014
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:31 pm

Until is /xx, if is, for example, /9 do not work.

Use everityme proper method.

why do you use brackets { } randomly and without any logical reason?
Last edited by rextended on Fri Mar 22, 2024 4:32 pm, edited 1 time in total.
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:31 pm

where you get the useless do {} and the other frills??????????
In a script, do you think I could drop the "do"?
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12014
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:35 pm

ignoring the way, the syntax is this:
:set currentIPv4 [/ip address get [find where interface=$wanInt] address] ; :set currentIPv4 [:pick $currentIPv4 0 ([:len $currentIPv4] - 3)]
:put $currentIPv4

1.2.3.4
obviously this do not consider the other script part you do not have pasted, like where $currentIPv4 and $wanInt is defined, etc.
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:47 pm

I see the limitations.
How do you propose to handle the single-digit mask lengths you mentioned above?
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12014
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:51 pm

 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Fri Mar 22, 2024 4:59 pm

[:find $wfaddress "/" -1]
vs
([:len $currentIPv4] - 3)
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Fri Mar 22, 2024 5:38 pm

Awsome...
Now it works great.
:do {:set currentIPv4 ([/ip address/get [find interface=$wanInt] address]); :set currentIPv4 [:pick $currentIPv4 0 [:find $currentIPv4 "/" -1]]};
Of course, this is just a snippet of a script. I appreciate all the valuable help.
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Fri Mar 22, 2024 6:40 pm

where you get the useless do {} and the other frills??????????
In a script, do you think I could drop the "do"?
Yes. Harmless, but unnecessary. Since :do { ... } is do'ing nothing. e.g. this is identical in function:
{:set currentIPv4 ([/ip address/get [find interface=$wanInt] address]); :set currentIPv4 [:pick $currentIPv4 0 [:find $currentIPv4 "/" -1]]}
if you want to keep it on one line.

And, yes, the ; at end is unneeded too. Newline ends a command (unless the backtick \ is used at end which mean the next line is part of this one). Semicolons only need if you want multiple commands on one line. So in your :do {} they are needed (since you don't have newlines). But there is no need to END a command with a semi-colon AND newline.

FWIW we're picky around here... since RouterOS script is also picky sometimes. :)
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Fri Mar 22, 2024 6:52 pm

You can also make it a function, if you need to do for a multiple items:
:global getInterfaceIP do={
   :local currentIPv4 [/ip address get [find interface~$1] address]
   :return [:pick $currentIPv4 0 [:find $currentIPv4 "/" -1]]
}

:put [$getInterfaceIP ether1]

Dorky side note: RouterOS has types too. So as a technical point ":return [:toip [:pick $currentIPv4 0 [:find $currentIPv4 "/" -1]]]" in above be more accurate but in nearly all contexts RouterOS will "cast" a string to IP if needed internally... so keep IP as a string likely easier in practice.
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Fri Mar 22, 2024 10:16 pm

I was mostly testing on the terminal, hence the one-liners.
For a clearer understanding, I guess I should use more indentations.

Thanks for the tips. Turning the commands into a function is very helpful, indeed.

Now, without a function, what if I were to use "on-error{}"? Would that require a "do{}" statement?
:do {
    :set currentIPv4 ([/ip address/get [find interface=$wanInt] address]);
    :set currentIPv4 [:pick $currentIPv4 0 [:find $currentIPv4 "/" -1]];
  } on-error={
    :log error "IPv4 was not detected from $wanInt interface"
  };
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Fri Mar 22, 2024 10:38 pm

Sure, that kinda more what's the :do is used for.

And, above code could fail, if an interface= had multiple IP address associated with it (e.g. "multihoming").

But.... it's often's better if a script fails immediately, than "catching" the on-error=. If something else further needs that IP address, which is likely, you've just moved what is going to fail & it's may hard to know that the issue is the IP is empty.
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Sat Mar 23, 2024 12:00 am

So, instead of
on-error={:log error ... };
I could try
on-error={
    :log error "IPv4 was not detected from $wanInt interface"
    :error "..."
};
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Sat Mar 23, 2024 2:38 am

Yup. :error will stop the script, and print message to the CLI/console. While the /log handles the case if same script run in background. So that combo covers both CLI and /system/script.

But often it better to just add some more :if (error-condition) do={:error ...} checks along the way. And if something actually goes to the on-error={}, your still left wondering what may have cause the error....

For example, in your code above (or my function too), using "[find interface=$wanInt]"... if you have one interface that has 2 /ip/address records – that cause an error on the "get". But... so would if there 0 /ip/address found for an interface. And... your on-error={} have no clue as which of those two case that was. Now a lot of times, the "why" doesn't matter too. But PREVENTING anything from going to the on-error= is a good idea.
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12014
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Isolate a value from a console output

Sat Mar 23, 2024 11:48 am

How to manage errors without using shitty on-error.

example code

{
    :global ipfrominterface do={
        :local iface [:tostr $1]
        :local ifadd 0.0.0.0
        /interface
        :if ([:len [find where name=$1]] != 1) do={:return "interface >$iface< do not exist"}
        /ip address
        :local temp [find where interface=$iface]
        :if ([:len $temp] = 0) do={:return "interface >$iface< do not have one address"}
        :if ([:len $temp] > 1) do={:return "interface >$iface< have more than one address"}
        :local ifadd [get $temp address]
        :return [:toip [:pick $ifadd 0 [:find $ifadd "/" -1]]]
    }

    :local iface    "ether1"
    :local ifaceadd [$ipfrominterface $iface]
    :if ([:type $ifaceadd] = "ip") do={
        :put "The IP address of $iface is $ifaceadd"
        :log info "The IP address of $iface is $ifaceadd"
    } else={
        :put "ERROR: $ifaceadd"
        :error $ifaceadd
    }
}
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Sat Mar 23, 2024 3:17 pm

Good example @rextended. Why I'm always pitching functions since you can hide all the checks inside so you don't need repeat elsewhere.

I'm not as adverse to on-error={} as @rextended BUT it is tricky... & in most cases your better off just letting things fail, or specifically checking every step with a :if () do={} instead.

And if you using recent V7 releases, in most cases :do {} on-error={} should be replaced be with
:onerror errtxt in={ # main code } do={:put $errtxt}
as you can at least get the actually get RouterOS's error text. And you cannot do that using :do {} on-error={} which was ONE of the reasons NOT to use :do {} on-error={} before – since there it "hides" RouterOS generated error message.
 
diasdm
newbie
Topic Author
Posts: 30
Joined: Fri Sep 22, 2023 4:48 pm

Re: Isolate a value from a console output

Sat Mar 23, 2024 3:25 pm

I see...
The code I'm aiming for is more concise and objective. It is good to know these limitations and possibilities.

BTW, I see that many over here are very picky, just like the OS.
But I don't mind the manners. I'm more interested in the content.

Thanks for all your feedback and insightful thoughts.
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Sat Mar 23, 2024 3:45 pm

It's that debugging later can be tricky. So you have to picky at the source. How anal you want to be is up to you. e.g. If your replacing the defconf, one error in a script, and the router won't work.

For example on scripting pickyness, you'll note the need parse the prefix part using :find and :pick above. Yet someone might think this would work using just a ":toip", after all you're fetch an /ip/address's address field:
:put [:toip [/ip/addresss/get ([find]->0) address] 

BUT the gets you a nil type. e.g.
:put [:typeof [:toip [/ip/address/get ([find]->0) address]]]
nil
# with out the :toip, it's a string
:put [:typeof [/ip/address/get ([find]->0) address]]
str
SEE... [:toip] doesn't deal with ip-prefix type (or strings that look like ip-prefix type) – it's just how :toip works. And might be difficult to figure out if :do () on-error={} was used. While an :if () do={} right below, you'd at least know the line that was a problem...

Now if you have a relatively fixed config, then "shortcutting" some checks may be totally fine. e.g. you know there is only one IP address & no one is going to rename interfaces – but then there should be no need for on-error={} either if that's true ;).
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12014
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Isolate a value from a console output

Sat Mar 23, 2024 4:32 pm

Is why on my script, I check all before
:return [:toip [:pick $ifadd 0 [:find $ifadd "/" -1]]]
And I'm pretty sure that $ifadd is one address (with or without "/n") so no nil or str is returned, but one ip...
Last edited by rextended on Sat Mar 23, 2024 4:40 pm, edited 2 times in total.
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

Re: Isolate a value from a console output

Sat Mar 23, 2024 4:37 pm

And I'm pretty sure that $ifadd is one address (with or without "/n") so no nil or str is returned, but one ip...
In your function, sure. My quick function example, no.

My point was that on-error={} offers a false sense of security – since there are a lot of oddities in scripting. And a lot of time RouterOS error message say stuff like "no item found" which is LOST if "caught" in an :do {} on-error={} and replaced with some :put "got an error here".
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12014
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: Isolate a value from a console output

Sat Mar 23, 2024 4:40 pm

Is why is important to check the data flow and prevent expected errors...

So, my script do all basic test like:
The interface exist?
The interface have no address?
The interface have multiple addresses?
After that checks, the interface exist, for sure, and have only one address, for sure, on format n.n.n.n/x where n is from 0 to 255 and x is from 1 to 32

Also normalizing the input on functions are important: that prevent other errors like interface name on input is not a string,
and on the output, if is one ip, is correct, unexpected string suggest that something goes wrong on function: without :toip the function return the string x.x.x.x
Checking if is one ip before the :toip do not matter, because "for sure" routeros do not allow invalid ips on ip address.... or not?....

Obviously on that microseconds can happen also that another IP is added, or the actual removed, or the interface deleted, etc. but this is absurd.

One example of the importance of timing is on removing the connections on connection tracking, often during execution some connections already do not exist, blocking the script.
on-error do not permit unblocked run, but appropriate search fix this problem...

Who is online

Users browsing this forum: No registered users and 4 guests