0

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

Jerry
  • 70,495
  • 13
  • 100
  • 144
James
  • 1,186
  • 9
  • 14

2 Answers2

1

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.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • That did the trick, thank you. Now I'm stumped by the fact that I'm unable to remove the trailing carriage return with `string trimright $next_hop` or even `string trimright $next_hop "\r"`. I checked documentation as old as Tcl 7.5 and string trimright command seems to have been there for a while... any thoughts? Trailing carriage return is just still there when I print my variable out. – James Oct 04 '13 at 14:18
  • hmm. if you *know* there's a CR there, you could use (deep breath) `[string range $next_hop 0 [expr {[string length $next_hop] - 2}]]` – glenn jackman Oct 04 '13 at 14:25
  • False alarm! I was doing something wrong elsewhere. string trimright was actually working, but I hadn't saved the trimmed version prior to printing... my Perl past, I guess. I'm all set now. Thanks a lot for your help. – James Oct 04 '13 at 15:21
  • 1
    Ah. The Tcl commands that take a variable's *value* cannot modify the variable itself (i.e., the `string` commands, `linsert`, `join`, etc) -- these commands return values that you have to save. Tcl commands that take a variable *name* **can** modifiy the contents of the variable (i.e. `lappend`, `append`, `incr`) – glenn jackman Oct 04 '13 at 15:26
1

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…

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • You're right this is old stuff... these guys take the "if it works don't touch it" approach to the next level. Can't do anything about that, unfortunately. Your [first] suggestion worked just fine, thanks (I don't need the Route metric... line). Now I'm fighting to remove the trailing \r, as you may have seen from my comment to the first answer. – James Oct 04 '13 at 14:24
  • @James I'm not too savvy about expect or the old version of Tcl, but do you get the carriage return if you use the regex `\[^\n\r\]*` instead of `\[^\n\]*`? – Jerry Oct 04 '13 at 15:04
  • Thanks, @Jerry... I had tried that but it did not match. But I'm all set now (see my comments to the above answer). Thanks for chipping in! – James Oct 04 '13 at 15:23
  • @James Oh, all right. Glad that you have this mystery solved =P – Jerry Oct 04 '13 at 15:25