Programmer
Programmer

Reputation: 8697

Expect : error can't read "ip": no such variable

I am a newbie in expect / TCL and trying to parse an HTML page that has output some thing like below:

<li><p>Timestamp: Wed, 14 Nov 2012 16:37:50 -0800
<li><p>Your IP address: 202.76.243.10</p></li>
<li><p class="XXX_no_wrap_overflow_hidden">Requested URL: /</p></li>
<li><p>Error reference number: 1003</p></li>
<li><p>Server ID: FL_23F7</p></li>
<li><p>Process ID: PID_1352939870.809-1-428432242</p></li>
<li><p>User-Agent: </p></li>

My script is below. I am able to get the web page which I am not able to parse the line "Your IP address:" which is giving me errors:

#!/usr/bin/expect -f
set timeout -1
spawn telnet www.whatismyip.com 80
send "GET /\r\n"
expect
set output $expect_out(buffer)
foreach line [split $output \n] {
        regexp {.*<li><p>Your IP Address Is:.*?(\d+\.\d+\.\d+\.\d+)} $line ip
        if {[string length ${ip}]} {
                puts $ip
    }
}

The error is:

    Connection closed by foreign host.
can't read "ip": no such variable
    while executing
"string length ${ip}"
    ("foreach" body line 3)
    invoked from within
"foreach line [split $output \n] {
        regexp {.*<li><p>Your IP Address Is:.*?(\d+\.\d+\.\d+\.\d+)} $line ip
        if {[string length ${ip}]} {
 ..."
    (file "./t4" line 7)

Any pointers where I am doing wrong?

Upvotes: 0

Views: 2518

Answers (3)

Hai Vu
Hai Vu

Reputation: 40688

If your ultimate goal is to get your host's external IP, then go with an API solution, such as one from exip.org:

#!/usr/bin/env tclsh

set api http://api-nyc01.exip.org/?call=ip
if {[catch {exec curl --silent $api} output]} {
    puts "Failed to acquire external IP"
} else {
    puts "My external IP is $output"
}

Please visit their API site for more information, especially if you live outside the USA. This solution requires curl, which you might need to install.

Upvotes: 0

Donal Fellows
Donal Fellows

Reputation: 137567

The regular expression did not match, so the variable was not assigned. You should check the result of regexp to see if the match succeeded; when not using the -all option to regexp, you can treat it like a boolean. Try this:

foreach line [split $output \n] {
    if {[regexp {<li><p>Your IP Address Is:.*?(\d+\.\d+\.\d+\.\d+)(?!\d)} $line -> ip]} {
        puts $ip
    }
}

The -> is really a (weird!) variable name which will hold the whole matched string; we're not interested in it (just the parenthetical part) so we use the non-alphabetic to mnemonically say “this is going to there” (the submatch to the ip variable).

Upvotes: 1

glenn jackman
glenn jackman

Reputation: 246764

Your line contains "address" (lowercase) but you're trying to match "Address" (uppercase). Add the
-nocase option to the regexp command. Also, Tcl regular expressions cannot have mixed greediness -- the first quantifier determines if the whole expression is greedy or non-greedy (I can't find where this is documented right now).

regexp -nocase {IP Address.*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})} $line -> ip

Upvotes: 1

Related Questions