bash shell脚本中的"set -o noglob"

Ann*_*awn 2 bash shell sqlplus inline

我通常会SQL在Bash shell脚本中内联编写语句,以便在SQLPlusas-中执行

#! /bin/sh

sqlplus user/pwd@dbname<<EOF
insert into dummy1 
select * from dummy2;

commit;
exit;
EOF
Run Code Online (Sandbox Code Playgroud)

这样可以正常工作,并dummy1在执行时插入行.我的一位同事前几天来找我,脚本如下(简化)

#! /bin/sh    
sqlvar="insert into dummy1 select * from dummy2;commit;"    
echo $sqlvar|sqlplus user/pwd@dbname
Run Code Online (Sandbox Code Playgroud)

这个问题是在执行变量时将变量sqlvar扩展*为当前目录中的所有文件,最终会出错 - 如

SQL> insert into dummy1 select <--all the file names in the current directory--> 
from dummy2;commit
                                                    *
ERROR at line 1:
ORA-00923: FROM keyword not found where expected
Run Code Online (Sandbox Code Playgroud)

我们对这一个的第一个立场是shell *在通配符上下文中解释并列出所有文件名,而shell变量扩展(不太确定为什么...... ???).所以,为了理解这一点,我们做了类似下面的事情 -

$ var="hello *"
$ echo $var
hello <--all the file names in the current directory-->
Run Code Online (Sandbox Code Playgroud)

$*
ksh: somefile.sh: 0403-006 Execute permission denied. #since it had no execute permission
Run Code Online (Sandbox Code Playgroud)

目录中有许多其他文件,我不确定为什么*选择执行somefile.sh或指向somefile.sh.

之后,我们意识到,使用set -o noglob会彻底解决这个问题,比如说 -

#! /bin/sh
set -o noglob
sqlvar="insert into dummy1 select * from dummy2;\n commit;"    
echo $sqlvar|sqlplus user/pwd@dbname
Run Code Online (Sandbox Code Playgroud)

有一些冲突或相当矛盾的描述setnoglob,在互联网上.所以我在寻找是否有人可以解释这一点的诀窍.

Joh*_*ica 10

之后,我们意识到,使用set -o noglob可以完全解决这个问题

它并没有解决这个问题,因为它隐藏了它.手头的问题是没有引用.引用变量通常是一种很好的做法,因为当变量包含特殊字符,空格等时,它会阻止shell执行意外操作.

禁用globbing确实可以防止*扩展,但这通常不是你想要做的事情.它可以让你使用*?,但如果你使用其他特殊字符的东西可能会断裂.

目录中有许多其他文件,我不确定为什么*选择执行somefile.sh或指向somefile.sh.

这里*扩展到当前目录中的所有文件名,然后此文件列表成为命令行.shell最终会尝试按字母顺序执行任何文件名.


所以,解决这个问题的正确方法是引用变量:

echo "$sqlvar" | sqlplus user/pwd@dbname
Run Code Online (Sandbox Code Playgroud)

这将解决通配符问题.另一个问题是您需要将\n转义序列解释为换行符.shell不会自动执行此操作.要开始\n工作要么使用echo -e:

echo -e "$sqlvar" | sqlplus user/pwd@dbname
Run Code Online (Sandbox Code Playgroud)

或者使用字符串文字语法$'...'.这是单引号,前面有一个美元符号.

sqlvar=$'insert into dummy1 select * from dummy2;\n commit;'
echo "$sqlvar" | sqlplus user/pwd@dbname
Run Code Online (Sandbox Code Playgroud)

(或删除换行符.)


Gor*_*son 6

在我开始之前:@John Kugelman的答案(正确的引用)是解决此问题的正确方法。设置noglob只能解决问题的某些变体,并在此过程中产生其他潜在问题。

但是,既然您问了什么set -o noglob,这里是ksh手册页的相关摘录(顺便说一句,您的标签说bash,但错误消息说ksh。我想您实际上是在使用ksh)。

noglob  Same as -f.

-f      Disables file name generation.

File Name Generation.
   Following splitting, each field is scanned for the characters *, ?,  (,
   and  [  unless  the -f option has been set.  If one of these characters
   appears, then the word is regarded as a pattern.  Each file name compo-
   nent  that  contains  any  pattern character is replaced with a lexico-
   graphically sorted set of names that  matches  the  pattern  from  that
   directory.
Run Code Online (Sandbox Code Playgroud)

那是什么意思呢?这是一个应该显示效果的简单示例:

$ echo *
file1 file2 file3 file4
$ ls *
file1 file2 file3 file4
$ *    # Note that this is equivalent to typing "file1 file2 file3 file4" as a command -- file1 is treated as the command (which doesn't exist), the rest as arguments to it
ksh: file1: not found
Run Code Online (Sandbox Code Playgroud)

现在看一下noglob设置的变化:

$ set -o noglob
$ echo *
*
$ ls *
ls: *: No such file or directory
$ *
ksh: *: not found
Run Code Online (Sandbox Code Playgroud)