为什么在没有 --interactive 的情况下运行 docker 容器时会失败

问题描述 投票:0回答:1

当我在 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 工作流、调用 Actions、运行大型容器、复杂的脚本,需要我的实际 expect 脚本)我认为这与 expect 正在运行的环境中有关没有stdin和/或stdout。直到我最终创建了这个简单得多的测试用例,我才找到错误的真正原因。

我不明白为什么

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)
docker tcl expect stdio
1个回答
1
投票

我偶然发现了实际答案

您可以使用 expect_before 和 expect_after,而不是将所有选项都添加到程序中的每个 expect 命令中。这些允许您设置替代序列,除了您已明确写入 expect 命令的那些之外,当前生成的进程中的每个后续 expect 命令都会做出反应。如果生成的命令返回的序列与多个备选方案相匹配,则采用的优先顺序为

expect_before
expect
expect_after

现在——请注意。 您应该在生成您希望控制的进程后指定任何 expect_before 和 expect_after 命令。 Expect 可以控制多个生成的进程,如果您在错误的位置指定了 _befores 和 _afters,您最终可能会将它们应用到错误的地方过程。 如果您根本没有派生进程并且您期望_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
© www.soinside.com 2019 - 2024. All rights reserved.