You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tcl/Expect脚本中两子进程能否同时输出到stdout?相关问题求助

问题描述

我编写了一个Expect脚本,流程如下:

  • 启动子进程test1.py(模拟certbot)
  • 解析其输出,提取数据后执行第二个子进程test2.py
  • 最后向第一个进程发送回车

遇到两个问题:

  1. 给test2.py添加日志输出后,用exec调用它时直接报错:
    while executing
    "exec ./test2.py "$SITE_DOMAIN" "$fileName" "$fileData""
    (file "./test.exp" line 23)
    
    删除test2.py的日志语句后恢复正常。
  2. 将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:02:02