Bash脚本中的Expect: expect_out不打印缓冲区



我试图通过登录到交换机来捕获"dir"命令的输出,但我无法这样做。我在Bash中使用Expect。我正在使用expect_out在缓冲区中捕获该命令的输出并将其打印出来。实际上,我想捕获输出并对其执行一些操作。

脚本:

#!/bin/bash
expect -c "
spawn telnet 1.1.1.1 2000
sleep 1
send "r"
send "r"
expect {
Prompt> { send "dirr"  }
}
set output $expect_out(buffer)
"
echo "$output"
输出:

spawn telnet 1.1.1.1 2000
Trying 1.1.1.1...
Connected to 1.1.1.1 (1.1.1.1).
Escape character is '^]'.


Prompt>
Prompt>

显示这些提示符后,脚本退出。我该如何解决这个问题?

分割后,我可以使用参数替换和单引号。现在我面临不同的错误。

脚本:

expect -c "
spawn telnet $IP $PORT1
sleep 1
send "r"
send "r"
"
expect -c '
expect {
Prompt> { send "dirr" }
set output $expect_out(buffer)
puts "$output"
}
'
输出:

spawn telnet 172.23.149.139 2033
can't read "expect_out(buffer)": no such variable
while executing
"expect {
Prompt> { send "dirr" }
set output $expect_out(buffer)
puts "$output"
}
"

我根据建议把它改成了。但我仍然面临着错误。

脚本:

output=$(expect -c '
spawn telnet '"$IP $PORT1"'
sleep 1
send '"r"'
send '"r"'
expect Prompt> { send '"dirr"'  }
expect '"n"'
expect -indices Prompt>
puts '"[string range $expect_out(buffer) 0 [expr $expect_out(0,end) - 1]]"'
')
echo "======="
echo "$output"
echo "======="
输出:

syntax error in expression "(0,end) - 1"
    while executing
"expr (0,end) - 1"
    invoked from within
"string range (buffer) 0 [expr (0,end) - 1]"
    invoked from within
"puts [string range (buffer) 0 [expr (0,end) - 1]]"
=======
spawn telnet 1.1.1.1 2000
Trying 1.1.1.1...
Connected to 1.1.1.1 (1.1.1.1).
Escape character is '^]'.


Prompt>
Prompt>
=======

因此为了避免错误,我更改了

puts '"[string range $expect_out(buffer) 0 [expr $expect_out(0,end) - 1]]"'

puts '"$expect_out(buffer)"'

但是我没有得到错误,但是dir的输出也没有被打印出来。比如:

Prompt>
Prompt> (buffer)

第二个" split " Expect程序不能访问派生的telnet进程。当你这样分割它们时,你使它们独立(一个不能访问另一个的变量或状态;实际上,当第二个进程启动第一个进程时,它的telnet进程已经不存在了。


shell将自动连接任何字符串(没有被未引号/未转义的空白分隔),而不考虑它们使用的引号类型(如果有的话)。这意味着您可以开始将Expect程序的第一部分放在单引号中,为参数替换切换到双引号,然后对程序的其余部分返回单引号(以避免在Expect代码中转义任何"$`)。

expect -c '
spawn telnet '"$HOST $PORT"'
sleep 1
⋮ (rest of Expect program)
'

它使用单引号来保护大部分程序,但又切换回双引号,让shell将其HOST和IP参数替换为Expect程序的文本。


接下来,启动expect的shell不能访问expect程序中设置的变量。从子进程收集输出的正常方法是让它写入标准输出或标准错误,并让shell通过命令替换($())收集输出。

在Tcl(和Expect)中,您可以使用puts将字符串发送到stdout。但是,默认情况下,Expect也会向stdout发送正常的"交互"输出(它从任何生成的命令接收到的内容以及它发送给它们的内容;例如,如果你手动运行衍生命令,你会看到什么)。您可以使用log_user 0禁用此默认日志记录。

你可以这样写你的程序:

#!/bin/sh
output=$(expect -c '
# suppress the display of the process interaction
log_user 0
spawn telnet '"$HOST $PORT"'
sleep 1
send "r"
send "r"
# after a prompt, send the interesting command
expect Prompt> { send "dirr"  }
# eat the n the remote end sent after we sent our r
expect "n"
# wait for the next prompt, saving its position in expect_out(buffer)
expect -indices Prompt>
# output what came after the command and before the next prompt
# (i.e. the output of the "dir" command)
puts [string range $expect_out(buffer) 
                   0 [expr $expect_out(0,start) - 1]]
')
echo "======="
echo "$output"
echo "======="

因为Expect脚本被括在双引号中,所以shell将$expect_out扩展为一个空字符串。把expect脚本的主体放在单引号中。

当您在Expect中设置变量时,shell将不知道。当Expect脚本完成时,它的变量也会消失。您必须puts的expect_out缓冲区

最新更新