Bash脚本执行MySQL命令报错,终端运行正常问题排查
我写了一个用于安装MySQL的Bash脚本,里面定义了log函数来执行命令并记录输出。脚本内容如下:
log() { echo -e " [$1]" >> output.txt # Execute the provided command $1 >> output.txt 2>&1 # Check the exit status of the command if [ $? -eq 0 ]; then echo -e "$2" else echo "Error on command: [$1]" fi } # #...... Mysql installation commands run in the same way as below # log 'sudo mysql -e "source /home/albi/Desktop/Bash_scripts/script.sql;"' "[CREATED MYSQL USER] "
执行脚本时,output.txt中出现错误:
ERROR 1049 (42000): Unknown database '/home/albi/Desktop/Bash_scripts/script.sql;"'
但直接在终端运行命令sudo mysql -e "source /home/albi/Desktop/Bash_scripts/script.sql;"却能成功执行。
问题原因
核心问题是Bash的参数解析机制:当你把带引号的命令作为单个字符串参数传给log函数,在函数内部用$1展开时,Bash会把引号当成普通字符,而非用来界定参数的语法符号。
具体来说,执行$1时,Bash会把命令拆分成:
sudo mysql -e "\"source" /home/albi/Desktop/Bash_scripts/script.sql;"\"
这就导致mysql把"source当成要执行的SQL命令,把/home/albi/Desktop/Bash_scripts/script.sql;"当成数据库名,因此抛出"Unknown database"错误。
而在终端直接运行时,Bash会正确解析引号,把"source /home/albi/Desktop/Bash_scripts/script.sql;"作为-e的单个参数传递给mysql,所以能正常执行。
解决建议
方法1:用eval重新解析命令
修改log函数,使用eval执行命令,它会重新解析整个命令字符串,正确处理引号:
log() { echo -e "\n[$1]" >> output.txt # 用eval解析命令字符串,保留引号的语法作用 eval "$1" >> output.txt 2>&1 if [ $? -eq 0 ]; then echo -e "$2" else echo "Error on command: [$1]" fi }
这种方法不需要修改原有的调用方式,适合快速修复。注意:如果命令来自不可信来源,eval可能有安全风险,但这里是自己编写的脚本,无需担心。
方法2:传递命令的拆分参数(更安全)
调整log函数的参数设计,把消息作为第一个参数,后续参数作为要执行的命令(用$@接收),这样Bash能直接正确解析每个参数,不需要依赖引号转义:
log() { local success_msg="$1" shift # 移除第一个参数(消息),剩下的就是命令参数 echo -e "\n[$*]" >> output.txt # 直接执行拆分后的命令参数 "$@" >> output.txt 2>&1 if [ $? -eq 0 ]; then echo -e "$success_msg" else echo "Error on command: [$*]" fi }
调用时调整参数顺序,不需要把整个命令用单引号包裹:
log "[CREATED MYSQL USER]" sudo mysql -e "source /home/albi/Desktop/Bash_scripts/script.sql;"
这种方法避免了eval的潜在风险,是更规范的Bash脚本写法。
内容的提问来源于stack exchange,提问作者Albii

