
Я хотел выполнить grep всех .txt-файлов, используя подстановочный символ '*'.
Я попробовал эту команду (а также без кавычек " "), но не получилось.
ls | grep "*.txt"
Интересно то, что если я добавлю в команду grep другой символ, соответствующий файлу .txt в каталоге, то это сработает
>>ls | grep s*.txt
sample.txt
Я знаю, что это ls *.txt
сработает, но я был немного поражен природой команды grep. Может ли кто-нибудь помочь мне, почему это происходит?
Может это из-за того, что grep использует регулярные выражения? Помогите, пожалуйста.
решение1
В регулярных выражениях *
означает «любое количество предыдущих элементов», а не «любое количество любых символов», как в шаблонах оболочки. И .
означает «любой отдельный символ». Таким образом, чтобы найти «что угодно, за которым следует литерал .txt
», вы бы использовали .*\.txt
. Или просто \.txt
, так как обычно сопоставления регулярных выражений ищут совпадение в любом месте строки. Затем снова \.txt
будет соответствовать имени файла, например foo.txtgz
, так как .txt
не обязательно должно быть в конце. Вам нужно будет \.txt$
заблокировать шаблон в конце строки.
Регулярное выражение *.txt
либо бессмысленно, либо ошибочно, либо ищет буквальную звездочку, в зависимости от реализации и от того, используете ли вы базовые регулярные выражения ( grep
) или расширенные регулярные выражения ( grep -E
). Лучше не использовать его.
С другой стороны, s*.txt
будет искать "любое количество букв s
, затем любой одиночный символ, затем литерал txt
". Это более допустимое регулярное выражение, но... все еще не соответствует sample.txt
.
Вместо этого, что происходит во второй команде, так это то, что поскольку s*.txt
не заключен в кавычки, оболочка расширяет s*.txt
до того, как grep
увидит его. Если единственным соответствующим файлом является sample.txt
, то grep
он ищет его в выводе ls
. (Если бы было несколько соответствующих имен файлов, первое было бы принято в качестве шаблона, а остальные — как имена файлов для grep
чтения. В этом случае он бы проигнорировал ввод из конвейера.)
Но, ls
также можно взять список файлов, так что, хотя вы могли бы использовать
ls | grep '\.txt'
чтобы получить любой .txt
файл, наверное, было бы проще просто использовать
ls *.txt
вместо.
решение2
Отчасти это связано с тем, что grep
он использует регулярные выражения (на самом деле, именно это и re
означает в названии — это сокращение отгглобальныйррегулярныйевыражениепринт).
Подстановочный *
знак в регулярных выражениях отличается от *
подстановочного знака в подстановке оболочки.
В регулярных выражениях *
означает «ноль или более ранее определенного объекта». Однако .
,такжеподстановочный знак, означающий «один символ».
В shell-глобах *
означает «ноль или более символов». .
Это вовсе не подстановочный знак.
При поиске grep
по шаблону "*.txt"
вы ищете ноль или более символов, за которыми следует еще один символ, а затем буквальная строка txt
.
Когда вы grep
для шаблона "s*.txt"m you are looking for a literal
s , followed by zero or more
s s, followed by any character, followed by the literal string
txt`.
Вот почему в регулярных выражениях вы часто найдете .*
, что означает «один любой символ, за которым следует ноль или более любых символов». Регулярное выражение для «буквально любая комбинация символов, кроме нуля символов».
Когда вы ls *.txt
сообщаете оболочке: «Найдите все имена файлов, соответствующие шаблону glob *.txt
, перечислите их здесь и укажите их в качестве аргументов команды» ls
.
решение3
обратите внимание, что grep ищет файлсодержаниев то время как первый аргумент - это ШАБЛОН поиска, а другие аргументы интерпретируются как ФАЙЛЫ для поиска
это станет более понятным для вас, если использовать grep -H -o
флаги или поместить их grep
внутрь скрипта и запустить его, bash -x script
чтобы увидеть, как расширяются подстановки оболочки перед передачей в качестве аргументов