指定sock连接MySQL命令行成功 但Shell脚本执行建库操作未生效
问题原因
这类“命令行手动执行正常、脚本跑同样参数没效果”的场景,核心坑点几乎都是sudo进程抢占了本该传给mysql的标准输入流。
原理说穿了很简单:
- 手动在交互式终端敲mysql命令时,输入流是直连终端的,输入的SQL直接被mysql进程拿到,没有中间环节。
- 当把
sudo和heredoc输入组合使用时,heredoc生成的内容是作为整个命令链的标准输入往下传的。但sudo默认不管是不是需要输密码、有没有免密权限,初始化的时候都会优先尝试从标准输入读数据做权限校验,这个过程会把heredoc里的SQL内容全部截走。最终mysql进程建立连接后,拿到的输入流是空的,等不到任何执行指令就直接退出,啥操作都不会做。 - 另外原脚本没有做错误捕获,mysql连接失败、执行出错的信息就算打在终端,也容易被当成普通输出忽略,导致误判脚本正常执行完成。
排查验证步骤
- 给原脚本加调试输出,定位实际执行环节的问题
把脚本改成下面的样子,打开shell的执行追踪打印,同时输出mysql的执行返回码,明确故障点:#!/bin/sh set -x /usr/bin/sudo mysql -u root -S /mnt/storagePool-1/storageSpace-1/.mysql/mysql3307/mysqld3307.sock <<EOF flush privileges; create database if not exists testaa CHARACTER SET utf8 COLLATE utf8_general_ci; quit EOF echo "mysql命令执行返回码:$?" exit 0 - 单独验证sudo是否吞掉输入内容
跑下面的测试命令,如果输出是空的,就实锤是sudo的输入截断问题:sudo cat <<EOF 测试输入内容 EOF - 绕开stdin环节交叉验证
把要执行的SQL写到临时文件,用文件重定向的方式喂给mysql,如果这个方式能成功建库,就完全确认是stdin被截断的问题:cat > /tmp/test.sql <<EOF flush privileges; create database if not exists testaa CHARACTER SET utf8 COLLATE utf8_general_ci; EOF /usr/bin/sudo mysql -u root -S /mnt/storagePool-1/storageSpace-1/.mysql/mysql3307/mysqld3307.sock < /tmp/test.sql
如果调试输出里看到mysql报无法连接sock文件、权限被拒的错误,就检查sock文件的属主权限、系统的AppArmor/SELinux规则是否拦截了非交互shell下的sock文件访问。
对应解决方法
- 给sudo加
-n参数,强制走纯非交互模式,禁止sudo从stdin读内容做密码校验,从根源上避免输入被吞,修改后的脚本片段:/usr/bin/sudo -n mysql -u root -S /mnt/storagePool-1/storageSpace-1/.mysql/mysql3307/mysqld3307.sock <<EOF flush privileges; create database if not exists testaa CHARACTER SET utf8 COLLATE utf8_general_ci; quit EOF - 如果脚本本身就是以root用户身份运行的,完全不需要sudo提权,直接把sudo去掉就行,多一层提权反而引入不必要的坑。
- 生产环境跑的SQL脚本,尽量不要用heredoc内联SQL,把SQL独立成单独的
.sql文件,通过文件重定向或者mysql的-e参数传入,既方便做语法校验、版本管理,也能避开各种输入流的坑。 - 脚本开头加
set -e,只要命令执行返回非0值就直接中断,避免静默执行失败的问题。
内容的提问来源于stack exchange,提问作者user12055965
相关产品推荐
相关产品推荐

