Shell-Script

如何使用正則表達式通過期望匹配以“$”結尾的內容?

  • July 15, 2020

我想通過使用對遠端伺服器執行一些命令expect。但是當我使用.*\$$它時,它總是無法匹配遠端提示來發送命令。

以下是我的腳本:

#!/bin/ksh
while read -r line; do
/usr/linux/bin/expect -c "
set timeout 20
spawn ssh padmin@$line
expect {
   -re \".*(P|p)assword:\" {send \"$2\r\"}
}
expect {
   -re \".*\$$\" {send \"ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r\"}
}
expect {
   -re \".*\$$\" {exit 0}
   eof {exit 0}
}
send_user \"$expect_out(1,string)\n\"
" 2>"$line".errlog &
done <"$1"

當我使用調試模式進行檢查時,我收到了以下消息:

expect version 5.45.4
spawn ssh padmin@192.168.0.1
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {3474502}
Gate keeper glob pattern for '(P|p)assword:' is ''. Not usable, disabling the performance booster.

expect: does "" (spawn_id exp4) match regular expression "(P|p)assword:"? (No Gate, RE only) gate=yes re=no
padmin@192.168.0.1's password: 
expect: does "padmin@192.168.0.1's password: " (spawn_id exp4) match regular expression "(P|p)assword:"? (No Gate, RE only) gate=yes re=yes
expect: set expect_out(0,string) "password:"
expect: set expect_out(1,string) "p"
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "padmin@192.168.0.1's password:"
send: sending "padmin\r" to { exp4 }
Gate keeper glob pattern for '^:.*$' is ':*'. Activating booster.

expect: does " " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=no
"send_user"? no


expect: does " \r\n" (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=no
"send_user"? no
Last unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5
Last login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4
Welcome!
/etc/profile[63]: hostname:  not found.

expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n" (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
"send_user"? no
:/home/padmin$ 
expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n:/home/padmin$ " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
"send_user"? no
expect: timed out
Gate keeper glob pattern for '^:.*$' is ':*'. Activating booster.

expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n:/home/padmin$ " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
sighandler: handling signal(2)
async event handler: Tcl_Eval(exit 130)

那麼我該如何解決這個問題呢?非常感謝。

讓我們看看傳遞給的字元串/usr/linux/bin/expect -c ...;該命令實際上收到了什麼?如果我們將命令替換為,echo我們可以看到預期的輸出:

expect {
   -re ".*$" {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print $9}'\r"}
}

現在讓我們對傳遞給的字元串做同樣的事情send。這次我們必須使用expect來解析字元串,所以我們用send一個expect命令替換,比如send_user

expect <<\!
send_user "ls -lt cfgbackups|grep gz|head -n 1|awk '{print $9}'\r"
!

這會產生預期的錯誤:

can't read "9": no such variable

所以我們需要停止期望$9通過使用來插值 , \$9

回到原來的 ksh 腳本,\在我們需要使用的雙引號字元串中生成一個\\. 所以最後,修復是:

expect {
   -re \".*$\" {send \"ls -lt cfgbackups|grep gz|head -n 1|awk '{print \\\$9}'\r\"}
}

如果單引號和雙引號之間沒有空格,您可以通過交替使用單引號和雙引號來減少此類問題。例如:echo "hi"'there'產生hithereecho '"who'"'s"'"'產生"who's"。並且 here-documents 的使用<<!<<\!样式提供了另一個級別的引用。

將期望程式碼混合到 shell 腳本中時,最好使用 heredoc。然後,您至少避免了一級引用地獄。在 Tcl/expect 中,正則表達式最好用大括號括起來而不是引用,這使得處理反斜杠更容易。

/usr/linux/bin/expect <<END_EXPECT 2>"$line".errlog &
   set timeout 20
   spawn ssh padmin@$line
   expect {
       -re {.*(P|p)assword:} {send "$2\r"}
   }
   expect {
       -re {.*\$$} {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r"}
   }
   expect {
       -re {.*\$$} {exit 0}
       eof {exit 0}
   }
   send_user "$expect_out(1,string)\n"
END_EXPECT

我不確定是否應該編寫換行符和輸入符,\r或者\\r-您可以嘗試一下。

您可以通過環境傳遞 shell 變數來期望,然後您可以引用整個 heredoc:

host="$line" passwd="$2" /usr/linux/bin/expect <<'END_EXPECT' 2>"$line".errlog &
   # ...........................................^..........^
   set timeout 20
   spawn ssh padmin@$env(host)
   expect {
       -re {.*(P|p)assword:} {send "$env(passwd)\r"}
   }
   expect {
       -re {.*\$$} {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r"}
   }
   expect {
       -re {.*\$$} {exit 0}
       eof {exit 0}
   }
   send_user "$expect_out(1,string)\n"
END_EXPECT

線路的目的是什麼send_user?您沒有以任何期望模式擷取任何內容,並且您實際上是在最後一個expect命令中退出期望。你覺得你會在那裡看到什麼?

引用自:https://unix.stackexchange.com/questions/597113