You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Shell脚本中禁用*通配符展开?使用noglob仍异常求助

问题分析与解决方案

你的问题核心在于命令替换的执行时机早于set -o noglob生效的时机,导致通配符*还是被展开了。让我一步步拆解原因,再给出修复方案:

为什么set -o noglob没起作用?

看你脚本里的这段代码:

{
    set -o noglob
    sed -e "s/#qry#/$(echo $(cat $infile))/g" <<!
...
!
}

Shell的执行逻辑是:在执行分组内的命令之前,会先解析整个命令行中的所有命令替换$(...))和变量展开。也就是说,$(echo $(cat $infile))这部分会在set -o noglob被执行之前就已经完成展开——这时候noglob还没生效,所以echo接收到的*会被当成通配符展开成当前目录的文件列表。

而你在命令行测试set -o noglobls *正常,是因为set -o noglob生效后才执行ls *,通配符展开被抑制了,和脚本里的执行顺序完全不同。

修复方案:先抑制通配符再读取SQL内容

我们需要调整顺序,先开启noglob再读取SQL文件内容,同时避免用echo $(cat ...)这种会触发通配符展开的写法。下面是优化后的脚本:

#!/bin/bash
if [[ "$1" == "-h" ]]
then
    echo "sqljob [sqlfile] [procnm] [host] [database] [config file]"
    echo "  sqlfile: text file containing an SQL statement"
    echo "  procnm: name that will given to the new, stored procedure"
    echo "  host: hostname of IP address of the database server"
    echo "  database: the procedure will be created here"
    echo "  config file: default configuration file with username and password"
    exit
fi

# 给变量加上引号,防止路径含空格时出错
infile="$1"
procnm="$2"
hn="$3"
pn="$4"
db="$5"
mycfg="$6"

# 第一步:开启noglob,读取SQL文件内容到变量(避免*被展开)
set -o noglob
sql_query=$(cat "$infile")
set +o noglob  # 用完后关闭,不影响后续命令

# 第二步:构建SQL模板(这里保持变量展开,符合你的需求)
sql_template=$(cat <<!
drop procedure if exists $procnm;
delete from jobs where jobname="$procnm";
insert into jobs set notes="SQL job $procnm", jobname="$procnm", parm_tmpl='int';
delimiter //
create procedure $procnm(vqid int)
begin
    call joblogmsg(vqid,0,"$procnm","","Executing #qry#");
    drop table if exists ${procnm}_res;
    create table ${procnm}_res as #qry#
end//
delimiter ;
!
)

# 第三步:用bash内置的字符串替换替换#qry#(比sed更安全,避免SQL里的/等特殊字符干扰)
final_sql=${sql_template//\#qry\#/$sql_query}

# 最后管道到mysql
echo "$final_sql" | mysql --defaults-file="$mycfg" -h "$hn" -P "$pn" "$db"

关键改进点:

  1. 提前开启noglob:在读取SQL文件前就开启set -o noglob,确保cat "$infile"输出的*不会被展开。读取完成后关闭noglob,避免影响后续命令。
  2. 直接读取文件到变量:不用echo $(cat ...)这种多余的写法,直接把文件内容存到变量里,避免shell拆分参数和通配符展开。
  3. 用bash内置替换代替sed:避免SQL语句中包含/&等sed特殊字符导致替换失败的问题,更稳定可靠。
  4. 给变量加引号:防止路径或参数含空格时出现意外错误。

这样修改后,SELECT *里的*就会被原样保留,不会被展开成文件列表了。

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

火山引擎 最新活动