如何使用正则表达式通过使用expect来匹配以“$”结尾的内容?

如何使用正则表达式通过使用expect来匹配以“$”结尾的内容?

我想使用 来对远程服务器执行一些命令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 [email protected]
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
[email protected]'s password: 
expect: does "[email protected]'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) "[email protected]'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)

那么我该如何解决这个问题呢?多谢。

答案1

/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'产生hithere,并echo '"who'"'s"'"'产生"who's"。此处文档的使用<<!和风格也提供了另一个层次的引用。<<\!

答案2

将expect代码混合到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 变量传递给expect,然后您可以引用整个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命令中退出了期望。你认为你会在那里看到什么?

答案3

我发现了问题。问题是$符号。当我使用\\\$预期输出时,脚本工作正常。因为$也是正则表达式中的特殊符号。当我们使用\$正则表达式时,会将其指示为正则表达式结束符号。所以我们需要使用\\\$将其转换为\$.就这样。多谢。

相关内容