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

Django调用的Bash脚本遇sudo超时挂起问题求助

问题排查与解决方案

核心原因分析

挂起的本质是脚本中的sudo命令在无终端(TTY)的环境下尝试请求密码,但Django进程(通常以www-data或其他系统用户运行)没有交互式终端,导致命令阻塞等待输入;且因为没有TTY,密码请求的提示无法输出到日志文件中。

另外你观察到的两个进程是正常现象:os.system()默认会调用sh -c执行命令,所以会出现sh -c bash_script.sh和脚本本身的/bin/bash bash_script.sh进程,和挂起问题无关。


排查步骤

  • 定位挂起的sudo命令:在脚本中每个sudo命令前后添加日志输出,比如:
    echo "Starting sudo command: [具体命令]" >> /tmp/sudo_debug.log
    sudo [具体命令]
    echo "Completed sudo command: [具体命令]" >> /tmp/sudo_debug.log
    
    运行后查看/tmp/sudo_debug.log,最后一条日志对应的就是挂起的sudo命令,确认该命令确实需要sudo权限。
  • 验证Django用户的sudo权限状态:切换到Django运行的用户(如www-data),执行:
    sudo -n true
    
    如果返回非0值,说明该用户没有免密sudo权限,且当前无有效sudo会话,这就是挂起的直接原因。

解决方案

方案1:配置免密sudo(推荐,最稳定)

彻底避免交互式密码请求:

  1. 用visudo编辑sudoers文件(必须用这个命令,防止语法错误破坏sudo功能):
    sudo visudo
    
  2. 添加以下内容(替换www-data为你的Django运行用户,/path/to/bash_script.sh为脚本绝对路径):
    www-data ALL=(ALL) NOPASSWD: /path/to/bash_script.sh
    # 若需要更精细控制,可只允许脚本中的特定命令:
    # www-data ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/systemctl
    
  3. 保存退出后,切换到Django用户测试:
    su - www-data
    sudo /path/to/bash_script.sh
    
    确认无需输入密码即可完整运行。

方案2:预先生成sudo会话(临时应急)

如果无法配置免密sudo,可在调用脚本前生成有效sudo会话:
修改Python调用代码,先执行sudo -v获取会话,再运行脚本:

import subprocess

# 先获取sudo会话(需确保Django用户可无TTY执行sudo -v,部分系统需额外配置)
subprocess.run(["sudo", "-v"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# 再运行脚本并记录日志
with open("bash_log.log", "w") as log_file:
    subprocess.run(["./bash_script.sh"], stdout=log_file, stderr=subprocess.STDOUT, check=False)

注意:此方式依赖sudo超时设置,会话过期后仍会出现挂起,稳定性不如方案1。

方案3:用pty模拟终端(不推荐,仅作极端应急)

若必须输入密码,可通过pty模块模拟终端传递密码,但明文存储密码存在严重安全风险,禁止生产环境使用:

import pty
import os

def run_sudo_script(script_path, password, log_path):
    def read(fd):
        data = os.read(fd, 1024)
        if b"[sudo] password for" in data:
            os.write(fd, password.encode() + b"\n")
        return data

    with open(log_path, "w") as log_file:
        pty.spawn(["./bash_script.sh"], read)

run_sudo_script("./bash_script.sh", "你的sudo密码", "bash_log.log")

无效方案的原因解释

  1. 脚本中添加sudo -v无效:脚本运行时处于无TTY环境,sudo -v同样会阻塞请求密码,无法刷新会话。
  2. 延长sudo超时无效:Django进程的环境和你手动ssh的环境是独立会话,超时设置不共享,手动延长的是你自己的会话,对Django进程无效。
  3. 拆分脚本多线程无效:挂起根源是sudo需要终端,拆分后第二部分的sudo命令依然处于无TTY环境,所以仍会挂起。

内容的提问来源于stack exchange,提问作者Jay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:24:15