Tcl/Expect脚本中两子进程能否同时输出到stdout?相关问题求助
问题描述
我编写了一个Expect脚本,流程如下:
- 启动子进程
test1.py(模拟certbot) - 解析其输出,提取数据后执行第二个子进程
test2.py - 最后向第一个进程发送回车
遇到两个问题:
- 给
test2.py添加日志输出后,用exec调用它时直接报错:
删除while executing "exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData"" (file "./test.exp" line 23)test2.py的日志语句后恢复正常。 - 将
exec改为spawn后,报错消失,但第一个进程的后续输出(比如test1.py最后打印的done)无法显示,最终只看到test2.py的Upload complete输出。
最小复现文件
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)
解决方案
问题1:exec调用报错的修复
exec在Expect中会捕获子进程的标准输出和错误,默认情况下如果子进程有输出,exec会将其返回,但未显式处理时会触发错误(Expect认为这是非预期输出)。
有两种修复方式:
- 方法1:将输出重定向到终端(保留日志,适合调试)
exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData" >@stdout 2>@stderr - 方法2:忽略所有输出(调试完成后可选)
exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData" >/dev/null 2>&1
问题2:spawn后无法看到第一个进程输出的修复
用spawn启动test2.py后,Expect的默认spawn_id会切换到新进程,后续的expect eof只会等待第二个进程结束,不会监听第一个进程的输出。
修复时需显式管理两个进程的spawn_id:
# 替换原exec语句为: spawn ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData" set upload_spawn_id $spawn_id # 等待test2.py执行完成 expect -i $upload_spawn_id eof # 切回certbot进程,发送回车 send -i $certbot_spawn_id "\r" # 等待certbot进程完成并捕获其输出 expect -i $certbot_spawn_id eof
内容的提问来源于stack exchange,提问作者soapergem
相关产品推荐
相关产品推荐

