当我在docker容器内运行expect脚本时,我得到了一个错误。
首先,我创建了一个简单的期望脚本test.expect
#!/usr/bin/expect -f
set timeout 3
#expect_before timeout { puts "nTimeout fired"; exit 1 }
puts "--- Start"
lassign $argv name duration
spawn bash -c "echo Hello ${name} && sleep ${duration} "
expect {
eof {
puts "No timeout";
}
}
puts "--- End"
注意:expect_before
首先被注释掉
现在docker镜像Dockerfile
FROM alpine:latest
RUN apk update &&
apk upgrade &&
apk add bash expect
COPY test.expect /root
ENV PATH $PATH:/root
WORKDIR /root
ENTRYPOINT ["test.expect"]
现在构建并运行docker:
$ docker build -t test-expect .
$ docker run -i test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run -i test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
--- End
$ docker run test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
--- End
无论是否使用docker-i
(--interactive
),脚本中的两个路径都可以正常工作。现在取消expect_before
的注释。重建并重新运行:
$ docker build -t test-expect .
$ docker run -i test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run -i test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
Timeout fired
$ docker run test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
error writing "stdout": bad file number
while executing
"puts "--- End""
(file "/root/test.expect" line 15)
$ docker run test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
error writing "stdout": bad file number
while executing
"puts "--- End""
(file "/root/test.expect" line 15)
自然地,当我第一次在我的实际管道(GitHub工作流,调用动作,运行大型容器,与复杂的脚本,需要我的实际期望脚本)中看到这一点时,我认为这与期望在没有标准输入和标准输出的环境中运行的事实有关。直到我最终创建了这个简单得多的测试用例,我才找到了错误的实际原因。
我无法理解为什么expect_before
会导致这种行为,更不用说我能做些什么来修复它了。
运行expect
和-d
没有给我任何洞察力
$ docker run test-expect Martin 5
expect version 5.45.4
argv[0] = /usr/bin/expect argv[1] = -df argv[2] = /root/test.expect argv[3] = Martin argv[4] = 5
set argc 2
set argv0 "/root/test.expect"
set argv "Martin 5"
executing commands from command file /root/test.expect
--- Start
spawn bash -c echo Hello Martin && sleep 5
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {7}
expect: read eof
expect: set expect_out(spawn_id) "exp0"
expect: set expect_out(buffer) ""
error writing "stdout": bad file number
while executing
"puts "--- End""
(file "/root/test.expect" line 15)
我偶然发现了真正的答案!
您可以使用expect_before和expect_after,而不是在程序中的每个expect命令中添加所有替代选项。这些允许您设置替代序列,除了您显式地写入expect命令中的序列之外,当前生成的进程中的每个后续expect命令将对这些序列作出反应。如果从生成的命令返回的序列匹配多个备选项,则优先级顺序为
expect_before expect expect_after
现在-一个警告。你应该指定任何expect_before和expect_after命令。Expect可以控制多个生成的进程,如果你在错误的地方指定了_befores和_afters,你最终可能会将它们应用到错误的进程。
如果您根本没有生成进程,并且expect_before,您可能会得到不稳定的结果-例如,一个应用程序交互运行,但从crontab失败或者在操作系统的一个版本上运行,但在下一个版本上失败。如果你没有意识到风险,很难识别这个问题!
所以修复非常简单:
#!/usr/bin/expect -f
set timeout 3
puts "--- Start"
lassign $argv name duration
spawn bash -c "echo Hello ${name} && sleep ${duration} "
expect_before timeout { puts "nTimeout fired"; exit 1 }
expect {
eof {
puts "No timeout";
}
}
puts "--- End"
现在无论-i
标志如何,它都可以工作
$ docker run -i test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run -i test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
Timeout fired
$ docker run test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
Timeout fired