私の理解によると、globワイルドカードはシェルによって解釈され、シェルは一致するファイル名ごとに指定されたコマンドを実行します。 abc1, abc2, and abc3
というファイルがあるとします。 私の現在のディレクトリにあります。次に、たとえば、echo abc*
「abc」で始まるファイル名ごとに1回エコーします。
ただし、grep 'foo' abc*
を実行すると 、これは実行する必要があると思います:
grep 'foo' abc1
grep 'foo' abc2
grep 'foo' abc3
つまり、次の出力を取得する必要があります(すべてのファイルに「foo」という1行が含まれていると仮定します):
foo
foo
foo
ただし、代わりに次のようになります:
abc1:foo
abc2:foo
abc3:foo
したがって、これには2つの考えられる説明があると思います。まず、どういうわけか、grepはglob式で使用されたことを検出し、一致する前にファイル名を出力することで応答します。次に、複数のファイルをgrepに渡すことができるため、シェルは実際には1つのコマンドのみを実行します。
grep 'foo' abc1 abc2 abc3
ただし、これが機能するのは、grepが最後に複数のファイルを受け入れるためです。別のコマンドでは1つのファイルしか渡されない可能性があります。したがって、グロブに一致する複数のファイルに対してコマンドを実行する場合、上記の2番目の方法でグロブが機能すると機能しません。
とにかく、誰かがこれに光を当てることができますか?
ありがとう!
承認された回答:
それが秘訣です。コマンドは認識していません。仕事をするのはシェルです
たとえば、grep 'abc' *.txt
について考えてみます。 。システムコールのトレースを実行すると、次のように表示されます。
bash-4.3$ strace -e trace=execve grep "abc" *.txt > /dev/null
execve("/bin/grep", ["grep", "abc", "ADDA_converters.txt", "after.txt", "altera_license.txt", "altera.txt", "ANALOG_DIGITAL_NOTES.txt", "androiddev.txt", "answer2.txt", "answer.txt", "ANSWER.txt", "ascii.txt", "askubuntu-profile.txt", "AskUbuntu_Translators.txt", "a.txt", "bash_result.txt", ...], [/* 80 vars */]) = 0
+++ exited with 0 +++
シェルが拡張された*.txt
.txt
で終わる現在のディレクトリ内のすべてのファイル名に 拡大。非常に効果的に、シェルはgrep 'abc' *.txt
を変換します grep 'abc' file1.txt file2.txt file3.txt . . .
。したがって、2番目の仮定は正しいです。
最初の仮定は正しくありません–プログラムにはグロブを検出する方法がありません。 *
を渡すことができます コマンドへの文字列引数として、しかしそれをどうするかを決めるのはコマンドの仕事です。ただし、ファイル名の拡張は、すでに述べたように、それぞれのシェルのプロパティです。
ただし、これが機能するのは、grepが最後に複数のファイルを受け入れるためです。別のコマンドでは、1つのファイルしか渡されない可能性があります。
まさにその通りです!プログラムは、受け入れ可能なコマンドライン引数の数を制限しません(たとえば、Cでは文字列の配列const char *args[]
Pythonではsys.argv[]
)、ただし、長さを検出できます その配列の、または予期しない何かが間違った配列位置にあるかどうか。 grep
これは行わず、複数のファイルを受け入れます。これは仕様によるものです。
ちなみに、不適切な引用とgrepを使用したグロビングが問題になる場合があります。これを考慮してください:
bash-4.3$ echo "one two" | strace -e trace=execve grep *est*
execve("/bin/grep", ["grep", "self_test.sh", "test.wxg"], [/* 80 vars */]) = 0
+++ exited with 1 +++
準備ができていないユーザーは、grepがest
の任意の行と一致することを期待します。 その中の文字はパイプから来ていますが、代わりにシェルのファイル名拡張がひねりを加えています。これは、ps aux | grep shell_script_name.sh
、プロセスが実行されていることを期待していますが、スクリプトがあった同じディレクトリからコマンドを実行したためです。 、シェルのファイル名拡張によりgrep
舞台裏でユーザーが期待したものとは完全に異なって見えるようにするコマンド。
適切な方法は、一重引用符を使用することです:
bash-4.3$ echo "one two" | strace -e trace=execve grep '*est*'
execve("/bin/grep", ["grep", "*est*"], [/* 80 vars */]) = 0
+++ exited with 1 +++