web-dev-qa-db-ja.com

コマンド(つまりgrep)は、glob展開の一部として実行されたときをどのように知るのですか?

私の理解では、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番目の方法でグロビングが機能した場合は機能しません。

とにかく、誰かがこれにいくらか光を当てることができますか?

ありがとう!

4
Cod3Citrus

それがコツです:コマンドは知らない、仕事をするのはシェルだ

たとえば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文字を含むすべての行に一致することを期待しますが、代わりにShellのファイル名の展開はあらゆるものをひねります。これは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 +++
5