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

如何在Bash脚本中为sudo tee使用-S选项(嵌入密码)

问题与解决:Bash脚本嵌入sudo密码写入systemd服务文件失败

问题描述

我尝试用Bash脚本创建systemd服务文件,使用如下函数:

function create_service_file() {

    # create service file
    echo "Creating service file /etc/systemd/system/${SERVICE_NAME}"
    cat <<- EOF | sudo tee /etc/systemd/system/${SERVICE_NAME}
        [Unit]
        Description=$DESCRIPTION
        After=syslog.target network.target

        [Service]
        User=$USER

        WorkingDirectory=${WORKING_DIRECTORY}

        ExecStart=${JAVA_HOME}/bin/java -jar ${JAR_FILE}
        ExecStop=/bin/kill -15 $MAINPID
        SuccessExitStatus=143

        Restart=always
        RestartSec=30s

        [Install]
        WantedBy=multi-user.target

        EOF
    echo "/etc/systemd/system/${SERVICE_NAME} file has been written successfully"
}

运行脚本时,cat <<- EOF | sudo tee /etc/systemd/system/${SERVICE_NAME}行会要求输入密码,输入后文件能正常写入。

我希望直接嵌入密码执行该命令,脚本其他部分使用local is_service_failed=$(sudo -S <<< "${PASSWORD}" systemctl is-active $service)或sudo -S <<< "${PASSWORD}" systemctl start $service能正常工作,但尝试cat <<- EOF | sudo -S <<< ${PASSWORD} tee /etc/systemd/system/${SERVICE_NAME}时,文件虽创建但无内容。请问哪里出错了?该如何解决?

错误原因与解决方法

错误原因

你当前的写法cat <<- EOF | sudo -S <<< ${PASSWORD} tee ...是把两个输入流同时传给sudo:一个是cat输出的服务文件内容,另一个是<<< ${PASSWORD}的密码。但sudo -S会先读取标准输入的密码,读取完成后才会执行后续命令,此时cat的输出已经被sudo当作密码的一部分(或被丢弃),导致tee没有接收到任何内容,所以文件为空。

正确写法

需要把密码和服务文件内容按顺序传给sudo的标准输入:先传密码,再传服务文件内容。以下是两种可行实现方式:

方法一:用子Shell合并输入流

( echo "${PASSWORD}"; cat <<- EOF
        [Unit]
        Description=$DESCRIPTION
        After=syslog.target network.target

        [Service]
        User=$USER

        WorkingDirectory=${WORKING_DIRECTORY}

        ExecStart=${JAVA_HOME}/bin/java -jar ${JAR_FILE}
        ExecStop=/bin/kill -15 \$MAINPID
        SuccessExitStatus=143

        Restart=always
        RestartSec=30s

        [Install]
        WantedBy=multi-user.target

EOF
) | sudo -S tee /etc/systemd/system/${SERVICE_NAME}

注意:$MAINPID需要转义为\$MAINPID,避免被当前Shell提前解析。

方法二:用printf合并密码和内容

printf "%s\n%s" "${PASSWORD}" "$(cat <<- EOF
        [Unit]
        Description=$DESCRIPTION
        After=syslog.target network.target

        [Service]
        User=$USER

        WorkingDirectory=${WORKING_DIRECTORY}

        ExecStart=${JAVA_HOME}/bin/java -jar ${JAR_FILE}
        ExecStop=/bin/kill -15 \$MAINPID
        SuccessExitStatus=143

        Restart=always
        RestartSec=30s

        [Install]
        WantedBy=multi-user.target

EOF
)" | sudo -S tee /etc/systemd/system/${SERVICE_NAME}

重要注意事项

  • 直接在脚本中硬编码密码存在严重安全风险,任何能读取脚本的用户都能获取密码,建议尽量避免这种写法。
  • 更安全的替代方案是配置sudoers文件,让执行该脚本的用户无需密码即可运行tee命令写入目标路径,或无需密码执行特定的systemd相关操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:36:32