Как команда (например, grep) узнает, когда она запускается как часть расширения glob?
Насколько я понимаю, подстановочный знак glob интерпретируется оболочкой, которая затем запускает данную команду для каждого соответствующего имени файла. Предположим, у меня есть файлы: abc1, abc2, and abc3
в моем текущем каталоге. Тогда, например, echo abc*
будет отображаться один раз для каждого имени файла, начинающегося с 'abc'.
Однако, если я бегу grep 'foo' abc*
Я полагаю, это должно работать:
grep 'foo' abc1
grep 'foo' abc2
grep 'foo' abc3
Это означает, что я должен получить следующий вывод (при условии, что все файлы содержат одну строку с надписью 'foo'):
foo
foo
foo
Однако вместо этого я получаю:
abc1:foo
abc2:foo
abc3:foo
Поэтому я полагаю, что есть 2 возможных объяснения этому. Во-первых, grep может обнаружить, что он использовался с выражениями glob, и отвечает, выводя имена файлов перед совпадениями. Во-вторых, поскольку вы можете передать несколько файлов в grep, оболочка фактически выполняет только 1 команду:
grep 'foo' abc1 abc2 abc3
Однако это работает только потому, что в конце grep принимает несколько файлов. Вполне возможно, что другая команда разрешит передачу только одного файла. Поэтому, если вы хотите выполнить команду для нескольких файлов, соответствующих глобанту, она не будет работать, если глобализация будет работать с помощью второго метода, описанного выше.
В любом случае, кто-то может пролить свет на это?
Спасибо!
1 ответ
Это хитрость: команда не знает, это оболочка, которая делает работу
Рассмотрим для примера 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 . . .
, Таким образом, ваше второе предположение верно.
Первое предположение неверно - программы не могут обнаружить глобус. Можно пройти *
в качестве строкового аргумента команды, но задача команды - решить, что с ней делать. Расширение имени файла, однако, является свойством вашей соответствующей оболочки, как я уже упоминал.
Однако это работает только потому, что в конце grep принимает несколько файлов. Вполне возможно, что другая команда позволит передать только 1 файл.
Абсолютно верно! Программы не ограничивают количество допустимых аргументов командной строки (например, в C это массив строк const char *args[]
и в питоне 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 +++