Question

New to the site, so bear with me. I'm working on a Tcl/Expect script and trying to match part of the 4th line in the following router output (two possible outputs shown). It will usually have an IP address, but may have a string like in the second sample:

Routing entry for 10.1.1.0/30
  Known via "static", distance 1, metric 0
  Routing Descriptor Blocks:
  * 10.3.3.1
      Route metric is 0, traffic share count is 1

Another possible output:

Routing entry for 10.1.2.0/24
  Known via "static", distance 220, metric 0 (connected)
  Advertised by bgp 1234
  Routing Descriptor Blocks:
  * directly connected, via Null0
      Route metric is 0, traffic share count is 1

My expect statement, using regexp, is the following:

expect -re "Routing Descriptor Blocks:\r\n  \\\* (.*)\r\n" {
        set next_hop $expect_out(1,string)
        puts "\n\n*Next-hop address is: $next_hop*\n"
}

(The 3 backslashes are so that they get through Tcl parsing, and a * is handed to the regexp interpreter, to match a literal asterisk.)

My problem is that - not surprisingly - this is doing a "greedy" match, and I need it NOT to be greedy. See debug output, where this is made clear:

expect: does "show ip route 10.1.1.0\r\nRouting entry for 10.1.1.0/30\r\n  Known via "static", distance 1, metric 0\r\n  Routing Descriptor Blocks:\r\n  * 10.3.3.1\r\n      Route metric is 0, traffic share count is 1\r\n\r\nRouter>" (spawn_id 4) match regular expression "Routing Descriptor Blocks:\r\n  \* (.*)\r\n"? yes
expect: set expect_out(0,string) "Routing Descriptor Blocks:\r\n  * 10.3.3.1\r\n   Route metric is 0, traffic share count is 1\r\n\r\n"
expect: set expect_out(1,string) "10.3.3.1\r\n      Route metric is 0, traffic share count is 1\r\n"

I would like the match to stop at the FIRST \r\n.

So, for non-greedy match, I would have thought I needed to add an "?" as follows:

expect -re "Routing Descriptor Blocks:\r\n  \\\* (.*?)\r\n" {
        set next_hop $expect_out(1,string)
        puts "\n\n*Next-hop address is: $next_hop*\n"
}

Problem is, this does not seem to work. I get the following from the debug output:

bad regular expression: nested *?+
    while executing
"expect -re "Routing Descriptor Blocks:\r\n  \\\* (.*?)\r\n" {
        set next_hop $expect_out(1,string)
        puts "\n\n*Next-hop address is: $next_hop*\n"
}"
    (file "./test_telnet_to_router.exp" line 23)

I've been staring at this for too long now, so thought I would request some help. Any ideas on what I need to do to get the lazy match I need? Pls note that I'm stuck with using just Basic Regular Expressions on this HP-UX server... Extended Regular Expressions are not available.

Thanks, James

Was it helpful?

Solution

Wow, that's old. Almost 20 years old. Any possibility you could upgrade?

One way to do a lazy match is to search for a greedy sequence of chars that are NOT a particular char. This might work

-re "Routing Descriptor Blocks:\r\n  \\\* (\[^\n\]+)\n"

The other choice is to do the greedy match and then split the captured part on newlines.

In either case, you'd have to remove the trailing carriage return manually.

OTHER TIPS

Tcl 7.4 is a real blast from the past, and it uses a (very old) version of the RE engine that doesn't support non-greedy REs at all. (The change to the RE engine happened in Tcl 8.0, which is still well over a decade old now. And out of support for a long time too…)

The simplest mechanism for working around the problem is to be much more specific in your regular expressions as to what you want to match. In particular, if you don't ever want a newline to be matched inside the captured section, don't use (.*) but rather use ([^\n]*). Since you're putting the RE in double-quotes, you'll actually need to use this:

expect -re "Routing Descriptor Blocks:\r\n  \\* (\[^\n\]*)\r\n" {
    set next_hop $expect_out(1,string)
    puts "\n\n*Next-hop address is: $next_hop*\n"
}

This is all assuming that you didn't want the Route metric… line. The easiest way to capture that if you do want it is to add another (no-newline-capturing) piece of RE on the end so it ends up as $expect_out(2,string).

expect -re "Routing Descriptor Blocks:\r\n  \\* (\[^\n\]*)\r\n *(\[^\n\]*)\r\n" {
    set next_hop $expect_out(1,string)
    puts "\n\n*Next-hop address is: $next_hop*\n"
    puts "Extra info: $expect_out(2,string)"
}

Generally, try to be as exact as possible with your REs when using Expect. It helps. But also remember that you can expect several different REs at once…

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top