如何在Bash脚本中为sudo tee使用-S选项(嵌入密码)
问题描述
我尝试用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

