如何让nohup后台运行的Python for循环脚本执行完全部迭代
问题诱因
- nohup启动命令未做完整的流重定向,也未将进程置于后台完全脱离终端控制。你当前执行的命令没有显式绑定stdin/stdout/stderr到文件或空设备,断开SSH连接后原终端的文件描述符会被系统回收:第一次gsutil传输完成后,后续循环启动新的gsutil进程时,尝试读取已失效的stdin会直接触发IO错误;如果脚本有打印耗时的逻辑,向已失效的stdout写入内容还会触发SIGPIPE信号,直接终止整个Python进程。
os.system()启动的gsutil子进程不会继承Python主进程的SIGHUP信号忽略规则。nohup仅会给它直接拉起的Python主进程配置SIGHUP屏蔽,os.system fork出的子进程仍会响应终端断开时广播的SIGHUP信号,第一次传输结束的间隙信号到达,就会直接终止主进程。- gsutil默认运行在交互模式,遇到文件冲突、权限异常、软链接跳过等场景时会弹出提示等待用户输入确认,在无交互终端的环境下,读输入的操作会直接返回错误,而原代码未做命令执行状态校验,错误发生后会直接中断整个循环。
修复方案
- 先调整Python脚本逻辑,替换
os.system为subprocess模块,显式配置子进程的流规则,关闭gsutil的交互提示,同时增加日志落盘逻辑,避免依赖终端输出:
import time import subprocess # 全局关闭gcloud/gsutil的所有交互提示 subprocess.run( ["gcloud", "config", "set", "core/disable_prompts", "True"], check=True, stdin=subprocess.DEVNULL ) bucket_lst = [] # 替换为你实际的存储桶列表 time_lst = [] for bucket in bucket_lst: start = time.time() # 按参数列表传命令,避免shell注入风险,显式重定向所有流 log_file = open(f"rsync_{bucket.replace('gs://', '').replace('/', '_')}.log", "a") result = subprocess.run( ["gsutil", "rsync", "-r", "/home/imagenet/tf_records", bucket], stdin=subprocess.DEVNULL, stdout=log_file, stderr=subprocess.STDOUT ) log_file.close() cost = time.time() - start time_lst.append(cost) # 耗时记录写入独立日志文件,不输出到终端 with open("transfer_time.log", "a") as f: f.write(f"{bucket} transfer finished, cost: {cost:.2f}s\n")
- 使用正确的nohup命令启动脚本,显式重定向所有流,让进程彻底脱离终端:
nohup python3 gce_to_gcs_throuhgput.py < /dev/null > run_total.log 2>&1 &
命令参数说明:
< /dev/null:将标准输入重定向到空设备,彻底断开和终端输入的关联,避免子进程读stdin触发错误> run_total.log 2>&1:将主进程的标准输出、标准错误统一写入run_total.log,避免终端断开后写输出触发SIGPIPE- 末尾的
&将进程放入后台运行,执行完命令后输入exit正常退出SSH会话即可,不要直接强制关闭终端窗口。
启动后可以执行ps aux | grep gce_to_gcs_throuhgput确认进程正常运行,确认无误后再退出连接。
内容的提问来源于stack exchange,提问作者DO Young Kim
相关产品推荐
相关产品推荐

