Регулярное выражение с использованием \\ vs using \

10

Почему

grep e\\.g\\. <<< "this is an e.g. wow"

и

grep e\.g\. <<< "this is an e.g. wow"

сделать то же самое?

Если я добавлю третью косую черту, это также даст тот же результат. НО, как только я добавляю четвертый слеш, он больше не работает. Это связано с вопросом из старого экзамена для класса. Он спросил, будет ли работать тот, у кого есть две обратные косые черты, чтобы вывести строку с «например», я изначально думал, что это не сработает, но я попытался убедиться, и это сработало. Какое объяснение?

Уайатт Грант
источник
Я думал, что bash возьмет \\\.и даст grep, \.но это не так. Хороший вопрос

Ответы:

9

Во-первых, обратите внимание, что одиночная косая черта слишком сильно совпадает:

$ echo $'eegg \n e.g.' | grep e\.g\.
eegg
 e.g.

Что касается Bash , то экранированный период совпадает с периодом. Bash проходит на период, чтобы grep . Для grep точка соответствует чему угодно.

Теперь рассмотрим:

$ echo $'eegg \n e.g.' | grep e\\.g\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\.g\\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\\.g\\\\.
$

Когда Bash видит двойную косую черту, он уменьшает ее до одной косой черты и передает ее на grep, который в первом из трех тестов, приведенных выше, видит, как мы хотим, одну косую черту перед точкой. Таким образом, это правильно делает.

С тройной косой чертой Bash уменьшает первые две до одной косой черты. Затем он видит \.. Поскольку экранированный период не имеет особого значения для Bash, он сводится к обычному периоду. В результате grep видит, как мы хотим, косую черту перед точкой.

С четырьмя слешами Bash уменьшает каждую пару до одного слеша. Bash передает grep две косые черты и точку. grep видит две косые черты и точку и уменьшает две косые черты до одного буквального слеша. Если за вводом нет буквального слеша, за которым следует какой-либо символ, совпадений нет.

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

$ echo 'eegg
e.g.
e\.g\.' |  grep e\\\\.g\\\\.
e\.g\.

Краткое изложение поведения Баша

Для Bash правила

  • Две косые черты уменьшаются до одной косой черты.

  • Косая черта перед нормальным символом, например точка, является просто нормальным символом (точка).

Таким образом:

$ echo \. \\. \\\. \\\\.
. \. \. \\.

Существует простой способ избежать этой путаницы: в командной строке Bash регулярные выражения следует помещать в одинарные кавычки. Внутри одинарных кавычек, Bash оставляет все в покое.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.
John1024
источник
Вопрос: bash требуется две обратные косые черты, чтобы увидеть их как обратную косую черту (одна - escape-последовательность, другая - буквальная обратная косая черта). Итак, когда их 3, bash также рассматривает третьего отставшего как escape-последовательность? Так как это ничего не ускользает, тогда отбрасывается?
Франц Кафка
@DanielAmaya Третий трактуется как побег для следующего персонажа. В нашем случае этот символ является периодом, а для bash (в отличие от grep) экранированный период является просто периодом. bash затем передает обычный период на grep.
John1024 20.10.14
@DanielAmaya Смотрите в обновленном ответе echoутверждение, иллюстрирующее, что делает bash в этих случаях.
John1024 20.10.14
2
@DanielAmaya В обоих случаях bash уменьшает первые две косые черты до одной косой черты. То, что остается, \.или .. Для bash оба они одинаковы: они эквивалентны обычному периоду. Следовательно, в целом то, что bash предоставляет grep, одинаково для обоих: одиночная косая черта, за которой следует точка.
John1024 20.10.14
1
Небольшое дополнение - использование echoне очень надежного способа тестирования регулярных выражений из-за множества реализаций этой программы. Например под моим zsh (встроенный echo) echo \. \\. \\\. \\\\. \\\\\.выдает . \. \. \. \., но /bin/echo \. \\. \\\. \\\\. \\\\\.возвращает . \. \. \\. \\.. Что-то вроде printf "%s" ..., вероятно, лучший способ.
Джимми
4

Вывод одинаков только для вашей строки, но в целом эти регулярные выражения делают разные вещи. Давайте немного изменим ваш пример, добавив второй шаблон e,g,(с запятыми), третий e\.g\.(точки), четвертый e\,g\,(запятые) и -oопцию grep для печати только соответствующих частей.

  • В следующем случае .соответствовать любому символу (уведомление ''вокруг e.g., я пришел к этому позже)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
  • Далее мы убегаем .с обратной косой чертой \, так .что будет сопоставлено только литерал :

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
  • Но мы можем убежать \с другим \, так что за литералом \будет следовать .(то есть любой символ):

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
  • Но если мы хотим сопоставить только \.не, \,тогда \нужен еще один, чтобы избежать особого значения точки:

    $ grep -o 'e\\\.g\\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.

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

grep 'e\.g\.'     => grep e\\.g\\.
grep 'e\\.g\\.'   => grep e\\\\.g\\\\.  (each backslash has to be quoted separately)
grep 'e\\\.g\\\.' => grep e\\\\\\.g\\\\\\. (3 x 2 = 6 backslashes in total)
jimmij
источник
3

Когда вы делаете a grep e\.g\., оболочка потребляет обратную косую черту, таким образом вы делаете a grep e.g., что соответствует. Когда вы делаете a grep e\\.g\\., оболочка снова потребляет косую черту, и теперь вы делаете a grep e\.\g., что снова совпадает. Теперь обратный слеш к оболочке выглядит следующим образом \\. Итак, когда у вас есть \\, первая - escape-последовательность, вторая - буквальная обратная косая черта. Когда вы делаете a grep e\\\.g\\\., оно все равно заканчивается тем grep e\.\g., что не существует escape-последовательности ( \) перед первой, \чтобы сделать ее литералом \. Имейте в виду, \ это обратная косая черта, и в grep e\\\\.\\\\gитоге получается grep e\\.g\\., что, очевидно, не совпадает.

Чтобы увидеть, как оболочка видит, что вы делаете, используйте echo (например, echo grep e\\.g\\. <<< "this is an e.g. wow"vs. echo grep e\\\\.g\\\\. <<< "this is an e.g. wow")

Франц Кафка
источник
0

Две команды выдают одинаковый вывод только для вашего ввода, но в остальном они различны. Чтобы понять, что происходит, мы должны знать, как параметр интерпретируется сначала, bashа затем - grep.

Спасаясь в Баш

\является специальным символом, который отменяет особое значение следующего символа, включая \самого себя. Если следующий символ не имеет специального значения, он передается без изменений. Примеры с командой и результатом:

  • echo \a: a- обычный персонаж сбежал дает символ
  • echo \\: \- экранированный специальный символ дает символ
  • echo \\\a: \a- комбинация особая, обычная
  • echo \\\\: \\- комбинация особая, особенная

echoнапечатает полученную строку после того, как bashинтерпретирует ее. Более подробная информация: Баш документация , Баш хакеров вики , спецификации POSIX .

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

  • echo .: .
  • echo \.: .
  • echo \\.: \.
  • echo \\\.: \.
  • echo \\\\.: \\.

Более простое решение для литеральных строк в bash

Чтобы передать параметры буквально, bashвы можете использовать одинарную кавычку '. Между одинарными кавычками вам не нужно заботиться об особом значении символов, потому что одинарные кавычки - это единственный символ с особым значением. Вы можете вставить одинарную кавычку после заключения первой части строки. Пример
echo 'part1'\''part2': part1'part2

Регулярное выражение в grep

\это экранирующий символ с таким же значением, как в bash. .это специальный символ, который представляет собой единственное вхождение любого символа . Смотрите: POSIX regex , GNU grep regex . Примеры регулярных выражений:

  • .- соответствует любому символу, как aили.
  • \.- соответствует только .буквально

Ваши примеры

На второй строке каждого примера ниже вы найдете эквивалент в одинарные кавычки , 'показывая , какие символьная строка передается по bashс grep. Затем после выполнения grepэкранирования единственным возможным специальным символом в примерах является .сопоставление с любым символом. В третьей строке есть описание, которому соответствует выражение.

  • grep e.g. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    eлюбой символ gлюбой символ - соответствует e.g.и, возможно, другие строки, такие какeagb
  • grep e\.g\. <<< "this is an e.g. wow"
    grep 'e.g.' <<< "this is an e.g. wow"
    eлюбой символ gлюбой символ - соответствует e.g.и, возможно, другие строки, такие какexgy
  • grep e\\.g\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.буквально - только совпаденияe.g.
  • grep e\\\.g\\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.буквально - только совпаденияe.g.
  • grep e\\\\.g\\\\. <<< "this is an e.g. wow"
    grep 'e\\.g\\.' <<< "this is an e.g. wow"
    e\любой символ g\любой символ - не совпадаетe.g.
pabouk
источник