Python脚本后台执行Shell命令并捕获日志的问题求助
Python调用Shell命令后台运行并收集日志的问题
需求
让Shell命令在后台运行并收集日志,同时Python脚本可继续执行后续步骤并正常退出,不被Shell命令阻塞。
已尝试的方法及问题
方法1
command=f"nohup ./run.sh > runLog/nohup.out &" subprocess.Popen(command, shell=True)
问题:Python脚本会等待进程结束,导致挂起,无法执行后续步骤。
方法2
command=f"nohup ./run.sh > runLog/nohup.out 2>&1 &" os.system(command)
问题:Python脚本能成功退出,Shell命令也在运行,但未生成日志文件,不符合需求。
新尝试方案
def job_run(command): try: #os.system(command) #subprocess.Popen(command, shell=True) cmds=shlex.split(command) p=subprocess.Popen(cmds, start_new_session=True) return "Success", f">>> Output of the command: ${command}" except Exception as e: return "Failure", f"??? An error occured: {e}" command=f"nohup ./run.sh > runLog/nohup.out &" result, output = job_run(command)
运行结果:
monitoring]$ nohup: ignoring input and appending output to 'nohup.out'
问题:脚本无法完全退出,需手动多次按回车才能结束执行。
解决方法及原理
核心问题分析
- 方法1中,
subprocess.Popen(shell=True)未显式重定向子进程的stdin/stdout/stderr,子进程与Python脚本共享终端,导致Python进程被阻塞。 - 方法2中,大概率是
runLog目录不存在或权限不足,导致日志文件无法生成;os.system虽因&让命令后台运行,但日志写入失败未被察觉。 - 新尝试的问题:
shlex.split将&拆为nohup的参数,而nohup不识别该参数;同时未重定向标准错误,nohup的提示信息输出到终端,干扰Python脚本正常退出。
正确实现方式
方案1:推荐(无shell=True,更安全)
import subprocess import os # 确保日志目录存在,不存在则创建 log_dir = "runLog" os.makedirs(log_dir, exist_ok=True) log_path = os.path.join(log_dir, "nohup.out") # 打开日志文件,将子进程的输出写入其中 with open(log_path, "w") as log_file: # 启动子进程,脱离当前会话,重定向输出 subprocess.Popen( ["./run.sh"], stdout=log_file, stderr=subprocess.STDOUT, # 把标准错误合并到标准输出,统一写入日志 start_new_session=True, # 子进程成为新会话 leader,Python退出后不会被终止 close_fds=True # 关闭继承的文件描述符,避免资源占用 ) # Python脚本可继续执行后续逻辑,最终正常退出 print("Shell命令已后台启动,Python脚本继续执行...")
方案2:使用shell=True(仅当必须通过shell解析命令时使用)
import subprocess import os log_dir = "runLog" os.makedirs(log_dir, exist_ok=True) command = f"nohup ./run.sh > {log_dir}/nohup.out 2>&1 &" subprocess.Popen( command, shell=True, stdout=subprocess.DEVNULL, # 丢弃子进程的标准输出,避免干扰终端 stderr=subprocess.DEVNULL, # 丢弃子进程的标准错误 start_new_session=True ) print("Shell命令已后台启动,Python脚本正常退出...")
关键注意事项
- 必须确保日志目录存在:用
os.makedirs(log_dir, exist_ok=True)避免因目录缺失导致日志写入失败。 - 分离子进程会话:
start_new_session=True让子进程脱离Python的会话组,防止Python退出时发送SIGHUP信号终止子进程。 - 显式重定向流:将子进程的
stdout/stderr定向到日志文件或DEVNULL,避免子进程与Python共享终端引发阻塞或输出干扰。 - 优先避免
shell=True:直接传递命令列表给Popen可避免shell注入风险,同时减少不必要的shell解析开销。
内容的提问来源于stack exchange,提问作者Pradyumn Joshi
相关产品推荐
相关产品推荐

