3

私は通常、次SQLのように実行されるBashシェルスクリプトにインラインでステートメントを記述しますSQLPlus

#! /bin/sh

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

commit;
exit;
EOF

これは問題なく機能し、実行時に行を挿入しますdummy1。先日、私の同僚が以下のようなスクリプトで私に来ました(簡略化)

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

これに関する問題は、実行時に変数が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

これに対する私たちの最初のスタンスは、シェルが*ワイルドカードコンテキストで解釈し、シェル変数の展開中にすべてのファイル名を一覧表示することでした(理由はよくわかりません.... ???)。それで、これを理解するために、私たちは以下のようなことをしました-

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

だが

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

ディレクトリには他にも多くのファイルがあり、なぜ*実行を選択したの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

インターネット上で、いくつかの矛盾した、またはかなり矛盾したsettingの説明がnoglobあります。だから私は誰かがこのビットのちょっとしたコツを説明できるかどうか探しています。

4

2 に答える 2

13

少し掘り下げた後、 set -o noglob を使用すると、この問題が完全に解決されることに気付きました

問題を隠すほど問題は解決しません。当面の問題は、引用の欠如です。変数に特殊文字や空白などが含まれている場合にシェルが予期しないことを行うのを防ぐため、変数を引用することは通常良い習慣です。

グロビングを無効にすると*、 が展開されなくなりますが、通常、これは実行したくないことです。*とを使用?できますが、他の特殊文字を使用すると問題が発生する可能性があります。

ディレクトリには他にも多くのファイルがあり、* が somefile.sh を実行することを選択した理由、または somefile.sh を指している理由がわかりません。

ここで*は、現在のディレクトリ内のすべてのファイル名に展開され、このファイル リストがコマンド ラインになります。シェルは、アルファベット順で最初にあるファイル名を実行しようとします。


したがって、これを修正する正しい方法は、変数を引用することです。

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

これにより、ワイルドカードの問題が解決されます。\nもう 1 つの問題は、エスケープ シーケンスを改行として解釈する必要があることです。シェルはこれを自動的に行いません。仕事に取り掛かるには、次\nのいずれかを使用しますecho -e

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

または、文字列リテラル構文を使用します$'...'。これは、前にドル記号が付いた単一引用符です。

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

(または改行を削除します。)

于 2012-09-01T05:51:40.003 に答える
7

始める前に: @John Kugelman の回答 (適切な引用) は、この問題を解決する正しい方法です。noglob を設定すると、問題のいくつかのバリアントのみが解決され、その過程で他の潜在的な問題が発生します。

しかし、あなたが何をするのか尋ねたのでset -o noglob、ここに ksh man ページからの関連する抜粋があります (ところで、タグには 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.

それで、それはどういう意味ですか?効果を示す簡単な例を次に示します。

$ 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

noglob セットで何が変わるか見てみましょう:

$ set -o noglob
$ echo *
*
$ ls *
ls: *: No such file or directory
$ *
ksh: *: not found
于 2012-09-01T07:31:57.950 に答える