จำนวนแบ็กสแลชที่จำเป็นสำหรับการหลบหนีแบ็กสแลช regex บนบรรทัดรับคำสั่ง


12

ฉันเพิ่งมีปัญหากับ regex บางอย่างในบรรทัดคำสั่งและพบว่าสำหรับการจับคู่แบ็กสแลชสามารถใช้อักขระต่าง ๆ ได้ หมายเลขนี้ขึ้นอยู่กับการอ้างอิงที่ใช้สำหรับ regex (ไม่มีเครื่องหมายคำพูดเดี่ยว, เครื่องหมายคำพูดคู่) ดูเซสชั่นทุบตีต่อไปนี้สำหรับสิ่งที่ฉันหมายถึง:

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 รายการได้

ฉันเข้าใจว่าหนึ่ง backslash พิเศษถูกละเว้นโดยเชลล์ (จากหน้า man bash):

"เครื่องหมายแบ็กสแลชที่ไม่ใช่เครื่องหมายอัญประกาศ (\) คืออักขระเลี่ยงมันจะเก็บรักษาค่าตามตัวอักษรของอักขระถัดไปที่ตามมา"

สิ่งนี้ใช้ไม่ได้กับตัวอย่างที่ยกมาเดี่ยวเนื่องจากไม่มีการหลบหนีในเครื่องหมายคำพูดเดี่ยว

และเครื่องหมายแบ็กสแลชเพิ่มเติมหนึ่งรายการจะถูกละเว้นโดยคำสั่ง grep ("\ c" เป็นเพียง "c" เท่านั้น แต่นี่ก็เหมือนกับ "c" เพราะ "c" ไม่มีความหมายพิเศษใน regex)

สิ่งนี้อธิบายพฤติกรรมของตัวอย่างด้วยเครื่องหมายคำพูดเดี่ยว แต่ฉันไม่เข้าใจอีกสองตัวอย่างโดยเฉพาะอย่างยิ่งว่าทำไมมีความแตกต่างระหว่างการที่ไม่ใช่ qouted สตริงที่มีเครื่องหมายคำพูดคู่

อ้างจากหน้า bash man อีกครั้ง:

"การใส่อักขระในเครื่องหมายคำพูดคู่จะเก็บรักษาค่าตัวอักษรของอักขระทั้งหมดภายในเครื่องหมายคำพูดยกเว้น $,`, \, และเมื่อเปิดใช้งานการขยายประวัติ,!. "

ฉันลองแบบเดียวกันกับ GNU awk (เช่นawk /ab\cd/{print} file) ด้วยผลลัพธ์เดียวกัน

Perl อย่างไรก็ตามแสดงผลลัพธ์ที่แตกต่าง (โดยใช้ตัวอย่างperl -ne "/ab\\cd/"\&\&print file):

  • ฉันสามารถจับคู่แบ็กสแลชกับแบ็กสแลชจริง 4-5 รายการได้
  • ด้วยเครื่องหมายคำพูดคู่ฉันสามารถจับคู่แบ็กสแลชกับแบ็คสแลชจริง 3-4 รายการ
  • ด้วยเครื่องหมายคำพูดเดี่ยวฉันสามารถจับคู่แบ็กสแลชกับ 2 แบ็คสแลชตามจริง

ใครสามารถอธิบายความแตกต่างระหว่างสตริง regex ที่ไม่ได้ยกมาและ double-qouted บนบรรทัดคำสั่งสำหรับ grep และ awk? ฉันไม่สนใจคำอธิบายเกี่ยวกับพฤติกรรมของ Perl เนื่องจากฉันมักจะไม่ใช้ Perl one-liners

คำตอบ:


10

สำหรับตัวอย่างที่ไม่มี\\เครื่องหมายคำพูดแต่ละคู่จะส่งแบ็กสแลชหนึ่งไปยัง grep ดังนั้นแบ็กสแลชทั้งสี่จะผ่านสองถึง grep ซึ่งแปลเป็นแบ็กสแลชเดี่ยว 6 backslashes ผ่านสามถึง grep แปลให้เป็นหนึ่งทับขวาและเป็นหนึ่งซึ่งมีค่าเท่ากับ\c cเครื่องหมายทับขวาหนึ่งอันจะไม่เปลี่ยนแปลงอะไรเลยเพราะมันถูกแปล\c-> cโดยเชลล์ แบ็กสแลชแปดตัวในเชลล์คือ grep สี่ตัวแปลเป็นสองค่าดังนั้นจึงไม่ตรงกันอีกต่อไป

สำหรับตัวอย่างในเครื่องหมายคำพูดคู่ให้สังเกตสิ่งที่ตามหลังเครื่องหมายคำพูดที่สองของคุณจาก bash manpage:

แบ็กสแลชจะคงความหมายพิเศษไว้เฉพาะเมื่อตามด้วยอักขระตัวใดตัวหนึ่งต่อไปนี้: $, `,", \, หรือบรรทัดใหม่

คือเมื่อคุณให้แบ็กสแลชเป็นจำนวนคี่ลำดับจะจบลง\cซึ่งจะเท่ากับcในกรณีที่ไม่มีเครื่องหมายอัญประกาศ แต่เมื่ออ้างอิงเครื่องหมายแบ็กสแลชจะสูญเสียความหมายพิเศษของมันดังนั้นจึง\cส่งผ่านไปยัง grep นั่นคือเหตุผลที่ช่วงของแบ็กสแลช "ที่เป็นไปได้" (เช่นที่ทำขึ้นที่รูปแบบที่ตรงกับไฟล์ตัวอย่างของคุณ) เลื่อนลงทีละอัน


... และจากนั้นก็มีบางสิ่งที่แปลกประหลาด: สำหรับตัวอย่าง: printf "\ntest"จะแทรกบรรทัดใหม่ก่อน "ทดสอบ" แม้ว่า"\n"ควรจะได้รับการแปล"n"โดยเชลล์ตามที่มันเป็น whithin อัญประกาศคู่ ... (ดังนั้นผลที่คาดหวังควรจะเป็นสำหรับ . "\ ntest", "ntest" เราควรจะได้รับนิสัยจะเขียนprintf "\\ntest"หรือ printf '\ntest'แต่อย่างใดฉันเห็นมากของสคริปต์อาศัยเหตุการณ์ที่แปลกประหลาดที่แทน.
โอลิเวีย Dulac

6

ลิงค์นี้จะอธิบาย bash Quotes and Escaping

คำถามของคุณเกี่ยวกับสามส่วนแรก

  • การหลบหนีต่อตัวละคร
  • การอ้างอิงที่อ่อนแอ "เครื่องหมายคำพูดคู่"
  • การอ้างอิงที่แข็งแกร่ง 'คำพูดเดียว'
  • ANSI C เช่นการอ้างอิงสตริง
  • I18N / L10N quoting (สากลและท้องถิ่น)

ด้านล่างนี้เป็นแผนภูมิที่แสดงให้เห็นว่าสตริงต่างๆbashส่งผ่านไปยังgrepอย่างไรและgrepจะตีความตีความภายในได้อย่างไร

echo "#ab\\cd" > fileช่วยให้ดูครั้งแรกที่
ในการที่อ่อนแอยกมา ( "") "#ab\\cd"ที่ \\เป็นหนี\ซึ่งจะถูกส่งไปเป็นตัวอักษรเดียวfile \ดังนั้นfileมี ab\cd

ตอนนี้สำหรับคำสั่งของคุณ: แผนภูมิด้านล่างอาจช่วยให้เห็นสิ่งที่เกิดขึ้นจริงกับการโทรแต่ละครั้ง *แสดงให้เห็นว่าคนที่ตรงกับเนื้อหาของแฟ้ม จริงๆแล้วมันเป็นเพียงเรื่องของการใช้กฎการหลบหนีของ bash เช่นเดียวกับบนหน้าเว็บโดยมีข้อความพิเศษให้กับคำตอบของแดเนียลคูลมานน์ซึ่งเขาหมายถึงพฤติกรรมการหลบหนีในสถานการณ์ที่ อ่อนแอ

แบ็กสแลชจะคงความหมายพิเศษไว้เฉพาะเมื่อตามด้วยอักขระตัวใดตัวหนึ่งต่อไปนี้: $, `,", \, หรือบรรทัดใหม่


                            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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.