bash shell 脚本中的“set -o noglob”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12225245/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 03:10:07  来源:igfitidea点击:

"set -o noglob" in bash shell script

bashshellinlinesqlplus

提问by AnBisw

I would usually write SQLstatements inline in a Bash shell script to be executed in SQLPlusas-

我通常会SQL在 Bash shell 脚本中编写内联语句,以作为SQLPlus-

#! /bin/sh

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

commit;
exit;
EOF

This would work just fine and will insert rows into dummy1when executed. A colleague of mine came to me the other day with a script like below (simplified)

这会工作得很好,并且会dummy1在执行时插入行。前几天,我的一位同事带着如下脚本(简化版)来找我

#! /bin/sh    
sqlvar="insert into dummy1 select * from dummy2;commit;"    
echo $sqlvar|sqlplus user/pwd@dbname

The issue with this is when executed the variable sqlvarexpands the *to be all the files in the current directory and would eventually error out like-

问题是执行时变量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

Our first stance on this one was the shell was interpreting *in a wildcard context and listing all the file names while the shell variable expansion (not quite sure why....???). So, in order to understand this we did something like below-

我们对此的第一个立场是 shell*在通配符上下文中进行解释,并在 shell 变量扩展时列出所有文件名(不太清楚为什么....???)。所以,为了理解这一点,我们做了如下的事情——

$ var="hello *"
$ echo $var
hello <--all the file names in the current directory-->

But

$*
ksh: somefile.sh: 0403-006 Execute permission denied. #since it had no execute permission

There are a number of other files in the directory and I am not sure why *chose to execute somefile.shor is pointed to somefile.sh.

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

After, a bit of digging we realized, using set -o noglobwould resolve this issue entirely, like-

之后,我们意识到,使用set -o noglob将完全解决此问题,例如-

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

There are some conflicting or rather contradictory description of setting noglob, over the internet. So I am looking if someone could explain the knick knacks of this bit.

互联网上有一些对setting 的相互矛盾或相当矛盾的描述noglob。所以我在寻找是否有人可以解释这一点的小窍门。

回答by John Kugelman

After, a bit of digging we realized, using set -o noglob would resolve this issue entirely

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

It doesn't resolve the issue so much as it hides it. The issue at hand is the lack of quoting. Quoting variables is usually a good practice as it prevents the shell from doing unexpected things when the variable contains special characters, whitespace, etc.

它并没有解决问题,因为它隐藏了它。目前的问题是缺乏引用。引用变量通常是一个很好的做法,因为它可以防止 shell 在变量包含特殊字符、空格等时做意外的事情。

Disabling globbing does prevent the *from being expanded, but that's generally not something you want to do. It'll let you use *and ?, but things could break if you used other special characters.

禁用通配符确实会阻止*扩展,但这通常不是您想要做的。它会让你使用*and ?,但如果你使用其他特殊字符,事情可能会中断。

There are a number of other files in the directory and I am not sure why * chose to execute somefile.sh or is pointed to somefile.sh.

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

Here *expands to all of the file names in the current directory, and then this file list becomes the command line. The shell ends up trying to execute whichever file name is first alphabetically.

这里*展开到当前目录下的所有文件名,然后这个文件列表就变成了命令行。shell 最终会尝试执行按字母顺序排列的第一个文件名。



So, the right way to fix this is to quote the variable:

因此,解决此问题的正确方法是引用变量:

echo "$sqlvar" | sqlplus user/pwd@dbname

That will solve the wildcard problem. The other issue is that you need the \nescape sequence to be interpreted as a newline. The shell doesn't do this automatically. To get \nto work either use echo -e:

这将解决通配符问题。另一个问题是您需要将\n转义序列解释为换行符。shell 不会自动执行此操作。要\n上班,请使用echo -e

echo -e "$sqlvar" | sqlplus user/pwd@dbname

Or use the string literal syntax $'...'. That's single quotes with a dollar sign in front.

或者使用字符串文字语法$'...'。那是前面带有美元符号的单引号。

sqlvar=$'insert into dummy1 select * from dummy2;\n commit;'
echo "$sqlvar" | sqlplus user/pwd@dbname

(Or delete the newline.)

(或删除换行符。)

回答by Gordon Davisson

Before I begin: @John Kugelman's answer (appropriate quoting) is the right way to solve this problem. Setting noglob only solves some variants of the problem, and creates other potential problems in the process.

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

But since you asked what set -o noglobdoes, here are the relevant excerpts from the ksh man page (BTW, your tags say bash, but the error message says ksh. I presume you're actually using ksh).

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

noglob??Same as -f.

noglob??Same as -f.

-f??????Disables file name generation.

-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.

So what does that mean? Here's a quick example that should show the effect:

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

$ 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

Now watch what changes with noglob set:

现在观察 noglob 设置的变化:

$ set -o noglob
$ echo *
*
$ ls *
ls: *: No such file or directory
$ *
ksh: *: not found