我编写了一个 Expect 脚本,它生成一个子进程 (certbot),解析输出,然后使用从第一个子进程提取的数据执行第二个子进程(Python 脚本),最后向第一个子进程发送回车符 (
\r
) 。我最近正在调试我的脚本的一个问题,所以我想我应该向 Python 脚本添加一些日志记录(以前不打印任何输出)。但是,这样做会导致期望脚本返回此错误:
while executing
"exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData""
(file "./test.exp" line 23)
然后我尝试将对第二个进程/Python 脚本的调用从
exec
调用更改为 spawn
调用。这部分有效并且消除了错误。但奇怪的是,我一直看到第一个进程的输出,直到我开始看到第二个进程的输出。一旦第二个进程开始记录消息,我就不再看到第一个进程的任何输出。
我在此处包含文件来说明作为 MRE 的问题:
main.exp
#!/usr/bin/expect
set SITE_DOMAIN $env(SITE_DOMAIN)
set SITE_EMAIL $env(SITE_EMAIL)
# Spawn the certbot process
spawn ./test1.py certonly -m "$SITE_EMAIL" --agree-tos --no-eff-email --force-renew --preferred-challenges http --manual -d "$SITE_DOMAIN" -d "www.$SITE_DOMAIN"
set certbot_spawn_id $spawn_id
# Expect the output starting with 40 hyphens and capture it
expect {
-re {(?n)^(- ){39}-\r\nCreate a file containing just this data:\r\n\r\n([\w.-]+)\r\n\r\nAnd make it available on your web server at this URL:\r\n\r\n.+/acme-challenge/([\w.-]+)} {
set fileData $expect_out(2,string)
set fileName $expect_out(3,string)
}
timeout {
"Error: output from certbot did not match the expected pattern"
exit 1
}
}
# Call a separate command with the parsed value
exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData"
# Send Enter to the certbot process to confirm
send -i $certbot_spawn_id "\r"
# Wait for the certbot process to finish
expect eof
test1.py
#!/usr/bin/env python
OUTPUT = """
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Account registered.
Requesting a certificate for example.com and www.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq.rstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVW
And make it available on your web server at this URL:
http://example.com/.well-known/acme-challenge/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"""
print(OUTPUT)
thing = input()
print("done")
test2.py
#!/usr/bin/env python
import logging
from time import sleep
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logging.basicConfig()
logger.info("Uploading file to web server")
sleep(2)
logger.info("Upload complete")
sleep(1)
因此,如果您按原样运行 main.exp,您将看到我提到的第一个错误。 (如果您从 test2.py 中删除日志语句,这些错误就会消失。)如果您将 main.exp 的第 22 行更改为使用
spawn
而不是 exec
,那也将修复错误,但是那么你就不会看到第一个进程输出的单词 done
。最后显示的内容将是 Upload complete
。
logging
将日志消息打印到 stderr。exec
如果看到任何输出到 stderr,则会引发错误。示例:
$ expect -c 'exec bash -c "echo hello >&2" '
hello
while executing
"exec bash -c "echo hello >&2" "
Tcl 的
exec
有选项 -ignorestderr
可以阻止它在这种情况下引发错误:
$ expect -c 'exec -ignorestderr bash -c "echo hello >&2" '
hello
来自 Tcl 的
exec
文档:
如果标准输出尚未重定向,则
命令返回最后一个的标准输出 管道中的命令,除非指定了exec
,在这种情况下也包括标准错误。 如果 管道中的任何命令异常退出或被终止或暂停,则2>@1
将返回错误 错误消息将包括管道的输出,后跟描述异常的错误消息 终止;exec
返回选项将包含有关最后一个异常的附加信息 遇到终止。 如果任何命令写入其标准错误文件,并且该标准错误不是 重定向且未指定-errorcode
,则-ignorestderr
exec
将返回错误;错误消息将包括 管道的标准输出,然后是有关异常终止的消息(如果有),然后是 标准错误输出。