如何捕获subprocess中带括号的bash脚本输出?
解决subprocess捕获子shell内输出的问题
我来帮你排查并解决这个问题——你遇到的核心痛点是子shell内的输出没被Python正确捕获,主要有几个原因:stderr未合并到stdout、缓冲机制导致输出延迟,还有脚本里一个小语法错误。下面一步步给你解决:
第一步:修复Shell脚本的问题
先看你的bash脚本,有两个关键问题需要调整:
1. 移除多余的;;语法
你的ssh命令后面多了一个;;,这是case语句的结束标记,在这里属于语法错误,会导致脚本执行失败,先删掉它。
2. 合并子shell的stderr到stdout,强制行缓冲
原来的() >&1只是把stdout重定向,没处理stderr(borg的很多输出比如警告、错误会走stderr);另外,bash在非终端环境下默认用块缓冲,导致输出不会实时刷新到Python。修改后的子shell代码如下:
( # ssh-agent的输出本来就不需要,扔去/dev/null没问题 eval $(ssh-agent) > /dev/null; ssh-add -q /home/borgs/.ssh/borg-client_key; # 用双引号包裹变量,避免特殊字符出错 echo "$borgPassword" | ssh -p "${port}" -A -o StrictHostKeyChecking=no "${user}@${ipAddress}" \ "BORG_PASSPHRASE=\$(cat) # 强制borg使用行缓冲,确保输出实时传递 stdbuf -oL borg --rsh 'ssh -p 9270 -o StrictHostKeyChecking=no' create --verbose --filter AME --list --stats --show-rc --compression lz4 --exclude-caches ssh://borgs@${ip_address}/~/${hostname}::${hostname}-${backupTime} ${locations}" # 确保SSH_AGENT_PID存在再kill,避免报错 [ -n "$SSH_AGENT_PID" ] && kill "$SSH_AGENT_PID"; ) 2>&1 # 把子shell的stderr合并到stdout
另外注意:脚本里的${ipAddress}和${ip_address}拼写不一致,要统一变量名,不然会导致ssh连接失败!
第二步:优化Python代码的输出捕获
你的Python代码只读取了stdout,而且没有处理缓冲问题,修改后让它同时捕获stdout和stderr,并且启用行缓冲:
def takeBackup(server, lg): print(f"Taking backup of {server['hostname']}") locationString = " ".join(server['locations']) cmd = [ "./create-backup.sh", server['hostname'], server['ip-address'], server['borgPassword'], locationString ] print(f"Running create-backup.sh command: {' '.join(cmd)}") # 关键配置:合并stderr到stdout,启用文本模式自动解码,行缓冲 process = subprocess.Popen( cmd, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, # 将stderr合并进stdout,统一捕获 stdout=subprocess.PIPE, text=True, # 自动把字节流转成字符串,不用手动decode() bufsize=1, # 启用行缓冲,配合shell的stdbuf实现实时输出 close_fds=True ) # 用iter()循环读取,直到EOF,比直接for line in process.stdout更可靠 for line in iter(process.stdout.readline, ''): line = line.rstrip('\n') lg.print_info(line) # 等待进程结束并获取返回码 returncode = process.wait() print(f"Backup process return code: {returncode}")
为什么这样修改能解决问题?
- 合并stderr到stdout:borg的很多日志(比如进度、警告)会输出到stderr,原来的Python代码没读取这部分,现在合并后就能全部捕获到。
- 强制行缓冲:
stdbuf -oL让borg每输出一行就立即刷新,Python的bufsize=1配合,确保输出能实时传递到logger,不会攒成块才输出。 - 修复语法错误:移除多余的
;;,避免脚本中途崩溃导致没有输出。 - 变量安全:用双引号包裹shell变量,避免空格、特殊字符导致命令执行异常。
内容的提问来源于stack exchange,提问作者IdecEddy
相关产品推荐
相关产品推荐

