Кількість зворотних косих рисок, необхідних для уникнення зворотної косої лінії в регулярному вираженні в командному рядку


12

Нещодавно у командному рядку виникли проблеми з деяким регулярним виразом, і я виявив, що для відповідності зворотної косої риси можна використовувати різні кількості символів. Це число залежить від цитування, використовуваного для регулярного виразу (жодного, одинарного, подвійного лапок). Про те, що я маю на увазі, див. Наступний сеанс баш:

echo "#ab\\cd" > file
grep -E ab\cd file
grep -E ab\\cd file
grep -E ab\\\cd file
grep -E ab\\\\cd file
#ab\cd
grep -E ab\\\\\cd file
#ab\cd
grep -E ab\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\\cd file
grep -E "ab\cd" file
grep -E "ab\\cd" file
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\\cd" file
#ab\cd
grep -E "ab\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\\cd" file
grep -E 'ab\cd' file
grep -E 'ab\\cd' file
#ab\cd
grep -E 'ab\\\cd' file
#ab\cd
grep -E 'ab\\\\cd' file

Це означає що:

  • без жодних лапок, я можу зіставити звороту косу рису з 4-7 фактичними косою рисою
  • з подвійними котируваннями я можу зіставити зворотний кут нахилу з 3-6 фактичними косою косою рисою
  • За допомогою одиничних лапок я можу зіставити зворотний кут нахилу з 2-3 фактичними косою рисою

Я розумію, що оболонка (зі сторінки bash man) ігнорує один додатковий зворотний кут:

"Нецитується зворотна косою рисою (\) - це символ втечі. Він зберігає буквальне значення наступного символу, який випливає"

Це не стосується прикладів, що цитуються одночасно, тому що в одних цитатах не робиться жодного виходу.

І ще одна зворотна косої риси ігнорується командою grep ("\ c" просто "c" уникнуто, але це точно так само, як "c", оскільки "c" не має особливого значення в регулярному вираженні).

Це пояснює поведінку прикладу з одинарними цитатами, але я не дуже розумію інші два приклади, особливо чому є різниця між нерекламованими рядками з подвійним цитуванням.

Знову цитата зі сторінки bash man:

"Закриття символів у подвійних лапках зберігає буквальне значення всіх символів у лапках, за винятком $,`, \ та, коли розширення історії включено,! "

Я спробував те ж саме з GNU awk (наприклад awk /ab\cd/{print} file), з тими ж результатами.

Однак Perl показує різні результати (використовуючи напр. perl -ne "/ab\\cd/"\&\&print file):

  • без жодних лапок, я можу зіставити звороту косу рису з 4-5 фактичними косою косою рисою
  • з подвійними котируваннями я можу зіставити зворотний кут нахилу з 3-4 фактичними косою рисою
  • За допомогою одиничних лапок я можу зіставити звороту косу рису з двома фактичними косою косою рисою

Чи може хтось пояснити цю різницю між нецитованими та дворядковими рядками регулярного виразів у командному рядку для grep та awk? Мене не так цікавить пояснення поведінки Perl, оскільки я зазвичай не використовую однолінійки Perl.

Відповіді:


10

Для прикладу, \\котрий не котирується, кожна пара передає одну зворотну косу рису в грепп, тому 4 косої косої риски передають двоє в греп, що переводиться на один косий ривок. 6 косої риски передають три на греп, переводячи на одну косою рисою та одну \c, що дорівнює c. Один додатковий косий ривок нічого не змінює, оскільки він перекладається \c-> cоболонкою. Вісім зворотних нахилів в оболонці чотири в греппі, переведені на два, так що це більше не відповідає.

Для прикладу в подвійних лапках зверніть увагу на те, що випливає з вашої другої цитати з сайту bash manpage:

Зворотна косої риси зберігає своє особливе значення лише тоді, коли слідує один із наступних символів: $, `,", \ або новий рядок.

Тобто, коли ви даєте непарну кількість косої риски, послідовність закінчується \c, що було б рівним cу випадку , котрий не котирується, але коли цитується, зворотна косої риси втрачає своє особливе значення, тому \cпередається в греп. Ось чому діапазон "можливих" косої риски (тобто тих, що складають шаблон, що відповідає вашому прикладу файлу) ковзає на одиницю.


... і тоді є деякі дивацтва: for example: printf "\ntest"вставить новий рядок перед "тестом", навіть якщо він "\n"повинен був бути перекладений "n"оболонкою, оскільки це подвійні лапки ... (тому очікуваний результат повинен бути, для "\ ntest", "ntest". Ми повинні отримати звичку писати: printf "\\ntest"або printf '\ntest', але я якось бачу багато сценарію, спираючись на дивацтва.
Олів'є Дулак,

6

На цьому посиланні описані bash Quotes та Escaping

Ваше питання стосується перших трьох розділів.

  • Персонаж втеча
  • Слабке цитування "подвійних цитат"
  • Сильне цитування "одиничних цитат"
  • ANSI C, як цитування рядків
  • Цитування I18N / L10N (Інтернаціоналізація та локалізація) .

Нижче наводиться діаграма того, як рядки, як bashпередає їх, grepі як grepдалі інтерпретувати їх внутрішньо.

Давайте спочатку подивимось echo "#ab\\cd" > file.
У слабкому котируванні ("") "#ab\\cd", \\це втеча, \яке передається fileяк єдиний буквальний \. Отже, fileмістить ab\cd

Тепер, до ваших команд: На наведеній нижче схемі може допомогти зрозуміти, що відбувається з кожним викликом. *Показує ті , які відповідають вмісту файлу. Це справді лише питання застосування правил втечі Баша , як на веб-сторінці, з особливою увагою до відповіді Даніеля Кулмана, де він посилається на втечу поведінки в ситуації слабкого цитування .

Зворотна косої риси зберігає своє особливе значення лише тоді, коли слідує один із наступних символів: $, `,", \ або новий рядок.


                            bash passes    grep further
                            to grep        resolves to         
grep -E ab\cd file            abcd           abcd   
grep -E ab\\cd file           ab\cd          abcd  
grep -E ab\\\cd file          ab\cd          abcd
grep -E ab\\\\cd file         ab\\cd         ab\cd    * 
grep -E ab\\\\\cd file        ab\\\cd        ab\cd    *
grep -E ab\\\\\\cd file       ab\\\cd        ab\cd    *    
grep -E ab\\\\\\\cd file      ab\\\cd        ab\cd    *
grep -E ab\\\\\\\\cd file     ab\\\\cd       ab\\cd

grep -E "ab\cd" file          ab\cd          abcd
grep -E "ab\\cd" file         ab\cd          abcd
grep -E "ab\\\cd" file        ab\\cd         ab\cd    *
grep -E "ab\\\\cd" file       ab\\cd         ab\cd    *
grep -E "ab\\\\\cd" file      ab\\\cd        ab\cd    *
grep -E "ab\\\\\\cd" file     ab\\\cd        ab\cd    *
grep -E "ab\\\\\\\cd" file    ab\\\\cd       ab\\cd    

grep -E 'ab\cd' file          ab\cd          abcd  
grep -E 'ab\\cd' file         ab\\cd         ab\cd    *
grep -E 'ab\\\cd' file        ab\\\cd        ab\cd    *
grep -E 'ab\\\\cd' file       ab\\\\cd       ab\\cd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.