Ожидайте: ошибка не может прочитать ip: нет такой переменной

Я новичок в ожидании/TCL и пытаюсь проанализировать HTML-страницу, которая выводит что-то вроде ниже:

<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>

Мой скрипт ниже. Я могу получить веб-страницу, на которой я не могу проанализировать строку «Ваш IP-адрес:», которая выдает мне ошибки:

#!/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
    }
}

Ошибка:

    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)

Любые указатели, где я делаю неправильно?


person Programmer    schedule 15.11.2012    source источник


Ответы (3)


Регулярное выражение не совпало, поэтому переменная не была присвоена. Вы должны проверить результат regexp, чтобы увидеть, удалось ли совпадение; когда не используется параметр -all для regexp, вы можете рассматривать его как логическое значение. Попробуй это:

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

-> на самом деле является (странным!) именем переменной, которая будет содержать всю совпадающую строку; нас это не интересует (только часть в скобках), поэтому мы используем неалфавитную часть, чтобы мнемонически сказать «это будет туда» (подсоответствие переменной ip).

person Donal Fellows    schedule 15.11.2012
comment
Спасибо, это работает, но значение IP усекается только последним значением символа. Мой код: if {[regexp {‹li›‹p›Ваш IP-адрес:.*?(\d+\.\d+\.\d+\.\d+)} $line -> ip]} { и если разметка is ‹li›‹p›Ваш IP-адрес: 202.76.243.10‹/p›‹/li› я получаю пут $ip как 202.76.243.1 - 0 в конце опущен - person Programmer; 15.11.2012
comment
Ах! Вас укусила нежадность. По действительно довольно неприятным техническим причинам все квантификаторы в этом RE не являются жадными: вам нужно убедиться, что RE правильно привязан, что делается путем ввода отрицательного прогноза ((?!\d): " рядом нет цифры») в конце RE. - person Donal Fellows; 15.11.2012
comment
«Действительно довольно неприятные технические причины» связаны с использованием Tcl теоретико-автоматного механизма RE вместо основанной на стеке системы PCRE и связанных с ней. Код, который это реализует, определенно нетривиален и пугающе сложен. Лично я стараюсь всегда писать исключительно жадные RE; так меня учили давным-давно, и их поведение (относительно) легко понять. - person Donal Fellows; 15.11.2012
comment
Первоначальный RE был тем, который вы предоставили, но с обрезанным начальным .*, так как он вам на самом деле не нужен, но это заставляло остальную часть RE интерпретироваться как жадная… и которую я не заметил. Ну что ж! - person Donal Fellows; 15.11.2012
comment
Кстати, попробуйте просто использовать http-пакет: package require http; set tok [http::geturl www.whatismyip.com]; if {[regexp {<li><p>Your IP Address Is:.*?(\d+\.\d+\.\d+\.\d+)(?!\d)} [http::data $tok] -> ip]} {puts $ip}; http::cleanup $tok - person Johannes Kuhn; 16.11.2012

Ваша строка содержит «адрес» (нижний регистр), но вы пытаетесь сопоставить «Адрес» (верхний регистр). Добавьте параметр
-nocase в команду регулярного выражения. Кроме того, регулярные выражения Tcl не могут иметь смешанную жадность — первый квантификатор определяет, является ли все выражение жадным или нежадным (сейчас я не могу найти, где это задокументировано).

regexp -nocase {IP Address.*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})} $line -> ip
person glenn jackman    schedule 15.11.2012
comment
То же значение, которое я получаю здесь - как уже упоминалось, значение ip усекается последним символом - если значением разметки является IP-адрес: 202.76.243.10‹/p›‹/li› я получаю «помещает $ip» как 202.76.243.1 - 0 в конце не учитывается - person Programmer; 15.11.2012

Если вашей конечной целью является получение внешнего IP-адреса вашего хоста, используйте решение API, например, от 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"
}

Посетите их сайт API для получения дополнительной информации, особенно если вы живете за пределами США. Для этого решения требуется curl, который вам может понадобиться установить.

person Hai Vu    schedule 15.11.2012