Python subprocess调用tcscid.exe仅返回stderr,无法获取stdout?
问题
我想用Python自动化Scid vs PC国际象棋软件的操作,该软件有命令行版本tcscid.exe。我用Python的subprocess模块调用它,代码如下:
import subprocess process = subprocess.Popen(["C:\\Scid vs PC-4.21\\bin\\tcscid.exe"],stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) command = 'sc_base create myfile' process.stdin.write(command) process.stdin.flush() #add more commands output, error = process.communicate() print("STDOUT output:") print(output) print("STDERR output:") print(error)
现在遇到两个核心问题:
- 需要在每条命令执行后检查错误,但不关闭程序时读取输出/错误流会导致代码崩溃。
- 用
communicate()也获取不到正常命令的stdout输出(手动在cmd里执行有正常输出),只有错误命令能拿到stderr信息,且命令实际是生效的(比如能成功创建文件)。
解决方案
1. 修复无stdout输出的问题
tcscid.exe是Tcl交互式程序,它需要每条命令末尾加换行符才会识别为命令输入完成,返回对应输出。修改命令写入代码:
command = 'sc_base create myfile\n' # 末尾添加换行符\n process.stdin.write(command) process.stdin.flush()
手动在cmd中输入命令时按回车,本质就是输入换行符告知程序命令结束,代码必须模拟这个行为。
2. 逐条命令安全读取输出/错误
直接读取流会因阻塞导致崩溃,可通过线程分别监听stdout和stderr流,避免单线程阻塞问题。示例代码:
import subprocess import threading import time def read_stream(stream, output_list): # 逐行读取流内容,直到流结束 for line in iter(stream.readline, ''): output_list.append(line.strip()) # 启动进程,设置行缓冲配合readline读取 process = subprocess.Popen( ["C:\\Scid vs PC-4.21\\bin\\tcscid.exe"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True ) # 存储输出和错误的列表 stdout_content = [] stderr_content = [] # 启动线程监听流 stdout_thread = threading.Thread(target=read_stream, args=(process.stdout, stdout_content)) stderr_thread = threading.Thread(target=read_stream, args=(process.stderr, stderr_content)) stdout_thread.daemon = True stderr_thread.daemon = True stdout_thread.start() stderr_thread.start() # 执行第一条命令 cmd1 = 'sc_base create myfile\n' process.stdin.write(cmd1) process.stdin.flush() # 等待命令执行完成(可根据实际场景调整等待时间,或判断输出内容) time.sleep(0.5) print("第一条命令STDOUT:", stdout_content) print("第一条命令STDERR:", stderr_content) # 清空列表准备执行下一条命令 stdout_content.clear() stderr_content.clear() # 执行第二条命令 cmd2 = 'sc_base list\n' process.stdin.write(cmd2) process.stdin.flush() time.sleep(0.5) print("第二条命令STDOUT:", stdout_content) print("第二条命令STDERR:", stderr_content) # 发送退出命令并关闭进程 process.stdin.write('quit\n') process.stdin.flush() process.wait()
说明:
bufsize=1设置行缓冲,让readline能及时获取输出。- 线程监听实现非阻塞读取,避免单线程下因一个流阻塞导致程序崩溃。
3. 更简便的替代方案:用Tcl脚本批量执行
由于tcscid.exe是Tcl程序,可编写Tcl脚本(比如cmds.tcl)批量执行命令:
sc_base create myfile # 写入其他需要执行的命令 quit
然后用Python调用执行该脚本:
import subprocess result = subprocess.run( ["C:\\Scid vs PC-4.21\\bin\\tcscid.exe", "cmds.tcl"], capture_output=True, text=True ) print("STDOUT:", result.stdout) print("STDERR:", result.stderr)
这种方式无需处理交互式流的阻塞问题,是tcscid.exe原生支持的批量执行方式,适合固定命令序列的场景。
内容的提问来源于stack exchange,提问作者tanler
相关产品推荐
相关产品推荐

