Проблема с использованием grep, чтобы соответствовать целому слову - PullRequest
0 голосов
/ 27 марта 2019

Я пытаюсь сопоставить всю строку в списке строк, разделенных новой строкой. Вот мой пример:

[hemanth.a@gateway ~]$ echo $snapshottableDirs
/user/hemanth.a/dummy1 /user/hemanth.a/dummy3
[hemanth.a@gateway ~]$ echo $snapshottableDirs | tr -s ' ' '\n'
/user/hemanth.a/dummy1
/user/hemanth.a/dummy3
[hemanth.a@gateway ~]$ echo $snapshottableDirs | tr -s ' ' '\n' | grep -w '/user/hemanth.a'
/user/hemanth.a/dummy1
/user/hemanth.a/dummy3

Моя цель - найти совпадение только в том случае, если строка /user/hemanth.a существует как целое слово (в новой строке) в списке строк. Но приведенная выше команда также возвращает строки, содержащие /user/hemanth.a.

Это пример сценария. Нет никакой гарантии, что все строки, которые я бы хотел сопоставить, будут иметь вид /user/xxxxxx.x. В идеале я хотел бы сопоставить точную строку, если она существует в новой строке как целое слово в списке.

Любая помощь будет оценена. спасибо.

Ответы [ 2 ]

3 голосов
/ 27 марта 2019

Обновление : использование fgrep -x '/user/hemanth.a', вероятно, является лучшим решением здесь, так как избегает необходимости экранировать такие символы, как $, чтобы grep не интерпретировал их как метасимволы. fgrep выполняет буквальное совпадение строк, а не совпадение с регулярным выражением, а опция -x указывает, что он соответствует только целым строкам.

Пример:

> cat testfile.txt
foo
foobar
barfoo
barfoobaz

> fgrep foo testfile.txt
foo
foobar
barfoo
barfoobaz

> fgrep -x foo testfile.txt
foo

Оригинальный ответ :

Попробуйте добавить метасимвол $ regex в конец выражения grep, например:

echo $snapshottableDirs | tr -s ' ' '\n' | grep -w '/user/hemanth.a$'. 

Метасимвол $ соответствует концу строки.

Пока вы это делаете, вы также можете использовать метасимвол ^, который соответствует началу строки, чтобы grep '/user/hemanth.a$' случайно не совпадал с чем-то вроде /user/foo/user/hemanth.a.

Итак, у вас будет это:

echo $snapshottableDirs | tr -s ' ' '\n' | grep '^/user/hemanth\.a$'. 

Редактировать : Возможно, вы на самом деле не хотите -w здесь, поэтому я удалил это из своего ответа.

Редактировать 2 : @U. Виндл поднимает хороший вопрос. Символ . в регулярном выражении является метасимволом, который соответствует любому символу, поэтому grep /user/hemanth.a может в конечном итоге соответствовать тем вещам, которые вы не ожидаете, таким как /user/hemanthxa и т. Д. Или, возможно, более вероятно также будет соответствовать строке /user/hemanth/a. Чтобы это исправить, вам нужно экранировать символ .. Я обновил строку grep выше, чтобы отразить это.

Обновление : В ответ на ваш вопрос в комментариях о том, как экранировать строку, чтобы ее можно было использовать в регулярном выражении grep ...

Да, вы можете экранировать строку, чтобы ее можно было использовать в регулярном выражении. Я объясню, как это сделать, но сначала я должен сказать, что попытка экранировать строки для использования в регулярном выражении может стать очень сложной с множеством странных крайних случаев. Например, экранированная строка, которая работает с grep, не обязательно будет работать с sed, awk, perl, оператором bash =~ или даже grep -e.

Кроме того, если вы перейдете от одинарных к двойным кавычкам, вам, возможно, придется добавить еще один уровень экранирования, чтобы bash правильно расширил вашу строку.

Например, если вы хотите найти литерал строку 'foo [bar]* baz$', используя grep, вам придется экранировать символы [, * и $, в результате чего регулярное выражение:

'foo \[bar]\* baz\$'

Но если по какой-то причине вы решили передать это выражение в grep в виде строки в двойных кавычках, вам нужно будет избежать побегов. В противном случае Bash будет интерпретировать некоторые из них как побег. Вы можете увидеть это, если вы делаете:

echo "foo \[bar]\* baz\$"
foo \[bar]\* baz$

Вы можете видеть, что bash интерпретировал \$ как escape-последовательность, представляющую символ $, и, таким образом, проглотил символ \. Это связано с тем, что обычно в строках с двойными кавычками $ - это специальный символ, который начинает раскрытие параметра. Но он оставил \[ и \* в одиночку, потому что [ и * не являются специальными внутри строки в двойных кавычках, поэтому он интерпретирует обратную косую черту как буквальные \ символы. Чтобы это выражение работало в качестве аргумента для grep в строке в двойных кавычках, вам потребуется экранировать последнюю обратную косую черту:

# This command prints nothing, because bash expands `\$` to just `$`,
# which grep then interprets as an end-of-line anchor.
> echo 'foo [bar]* baz$' | grep "foo \[bar]\* baz\$"

# Escaping the last backslash causes bash to expand `\\$` to `\$`,
# which grep then interprets as matching a literal $ character
> echo 'foo [bar]* baz$' | grep "foo \[bar]\* baz\\$"
foo [bar]* baz$

Но учтите, что "foo \[bar]\* baz \\$" будет не работать с sed, потому что sed использует другой синтаксис регулярного выражения, в котором экранирование [ приводит к , превращается в a метасимвол, тогда как в grep вы должны экранировать его до , чтобы не интерпретировал его как метасимвол.

Итак, еще раз, да, вы можете экранировать литеральную строку для использования в качестве grep регулярного выражения. Но если вам нужно сопоставить литеральные строки, содержащие символы, которые нужно экранировать, оказывается, что есть лучший способ: fgrep.

Команда fgrep на самом деле является просто сокращением для grep -F, где -F указывает grep на совпадение с «фиксированными строками» вместо регулярного выражения. Например:

> echo '[(*\^]$' | fgrep '[(*\^]$'
[(*\^]$

Это работает, потому что fgrep не знает или не заботится о регулярных выражениях.Он просто ищет точную буквенную строку '[(*\^]$'.Тем не менее, этот вид ставит вас на первое место, потому что fgrep будет соответствовать подстрокам:

> echo '/users/hemanth/dummy' | fgrep '/users/hemanth'
/users/hemanth/dummy

К счастью, есть способ обойти это, который, как оказалось, был лучшим подходом, чем мой первоначальныйответ, учитывая ваши конкретные потребности.Параметр -x для fgrep указывает, что он соответствует только всей строке.Обратите внимание, что -x не является специфическим для fgrep (поскольку fgrep на самом деле просто grep -F в любом случае).Например:

> echo '/users/hemanth/dummy' | fgrep -x '/users/hemanth' # prints nothing

Это эквивалентно тому, что вы получили бы, избежав регулярного выражения grep, и почти наверняка лучше, чем мой предыдущий ответ о включении вашего регулярного выражения в ^ и $.

Теперь, как и было обещано, на всякий случай, если вы захотите пойти по этому пути, вот как вы можете избежать фиксированной строки для использования в качестве grep регулярного выражения:

# Suppose we want to match the literal string '^foo.\ [bar]* baz$'
# It contains lots of stuff that grep would normally interpret as
# regular expression meta-characters. We need to escape those characters
# so grep will interpret them as literals.
> str='^foo.\ [bar]* baz$'
> echo "$str"
^foo.\ [bar]* baz$

> regex=$(sed -E 's,[.*^$\\[],\\&' <<< "$str")
> echo "$regex"
\^foo\.\\ \[bar]\* baz\$

> echo "$str" | grep "$regex"
^foo.\ [bar]* baz$
# Success

Опять же, по причинам, указанным выше, я не рекомендую такой подход, особенно если fgrep -x существует.

1 голос
/ 27 марта 2019

Прочитайте «Закрепление» в man grep:

   Anchoring
       The caret ^ and the dollar sign $ are meta-characters that respectively
       match the empty string at the beginning and end of a line.

Также помните, что . соответствует любому символу (с указанной страницы руководства):

The period . matches any single character.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...