Регулярне вираження, використовуючи \\ проти використання \


10

Чому

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

і

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

робити те саме?

Якщо я додам третю косу рису, вона також має такий же результат. Але, як тільки я додаю четверту косу рису, вона більше не працює. Це пов'язане з питанням із старого іспиту для класу. Він запитав, чи буде працювати один з двома косими рисочками для виведення рядка з "напр.", Я спочатку думав, що це не спрацює, але я намагався переконатись у цьому. Яке пояснення?


Я думав, що Баш візьме \\\.і дасть греп, \.але це не так. гарне запитання

Відповіді:


9

По-перше, зауважте, що одна косою рискою збігається занадто багато:

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

Що стосується Баша , то період, що втік, такий самий, як і період. Баш переходить на період, щоб грипнути . Для грепу період відповідає будь-якому.

Тепер розглянемо:

$ 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 зменшує кожну пару до одного косого кута. Bash переходить на грейп двох косих і періоду. grep бачить дві косою рискою та періодом і зводить дві косої риски до одного прямого прорізу. Якщо вхід не має прямого косого кута, який слід за будь-яким символом, немає відповідностей.

Щоб проілюструвати це останнє, пам’ятайте, що всередині одинарних лапок усі символи є буквальними. Таким чином, з урахуванням наступних трьох рядків введення команда grep збігається лише в рядку з прямолінійною косою рисою на вході:

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

Підсумок поведінки Баша

Для Баша правила такі

  • Дві косої риски зводяться до однієї косої риски.

  • Нахил перед нормальним персонажем, як і період, - це просто нормальний персонаж (період).

Таким чином:

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

Існує простий спосіб уникнути всієї цієї плутанини: у командному рядку Bash регулярні вирази повинні розміщуватися в одноцитати. Всередині одинарних цитат Баш залишає все в спокої.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.

Запитання: Для отримання bash потрібно дві зворотні косої риски, щоб побачити її як зворотну косу рису (одна - послідовність евакуації, інша - буквальна відмінка). Отже, коли є 3, чи баш трактує третього стражника як послідовність втечі? Оскільки вона нічого не врятує, то її потім відкидають?
Франц Кафка

@DanielAmaya Третя трактується як втеча для персонажа, що випливає. У нашому випадку цей персонаж є періодом, а для bash (на відміну від grep) період, що вийшов, - це просто звичайний період. Баш потім переходить рівний період на греп.
John1024

@DanielAmaya Ознайомтесь із оновленою відповіддю на echoвислів, який ілюструє, що баш робить у цих випадках.
John1024

2
@DanielAmaya В обох випадках bash зводить перші два косої риски до однієї косої риски. Те, що залишається, є \.або .. Для Баша обидва ці однакові: вони еквівалентні простому періоду. Отже, загалом те, що баш приносить греп, є однаковим для обох: односхилий наступний період.
John1024

1
Лише невелике доповнення - використання echoне дуже надійного способу тестування regexp через багато реалізацій цієї програми. Наприклад, під моїм zsh (вбудований ехо) echo \. \\. \\\. \\\\. \\\\\.дає . \. \. \. \., але /bin/echo \. \\. \\\. \\\\. \\\\\.повертає . \. \. \\. \\.. Щось подібне printf "%s" ...- це, мабуть, кращий спосіб.
jimmij

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)

3

Коли ви робите grep e\.g\., оболонка споживає зворотну косу рису, таким чином, ви робите те grep e.g., що відповідає. Коли ви робите a grep e\\.g\\., оболонка знову споживає косу рису, і тепер ви робите це grep e\.\g., яке знову відповідає. Тепер виглядає зворотний нахил до оболонки \\. Отже, коли у вас є \\, перше - це послідовність втечі, друге - буквальне зворотне нахил. Коли ви робите a grep e\\\.g\\\., він все ще закінчується буттям grep e\.\g., оскільки не існує послідовності евакуації ( \) перед першою, \щоб зробити це буквальним \. Майте на увазі , \ є зворотною косою риси, таким чином , grep e\\\\.\\\\gзакінчує тим grep e\\.g\\., що явно не відповідає.

Щоб побачити, як оболонка бачить, що ви робите, використовуйте ехо (наприклад, echo grep e\\.g\\. <<< "this is an e.g. wow"проти 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

Регекс у греп

\є символом втечі з подібним значенням, як у bash. .це спеціальний символ, який представляє собою одиночне виникнення будь-якого символу . Див.: Регулярний вираз POSIX , 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.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.