นิพจน์ทั่วไปที่ใช้ \\ vs using \


10

ทำไม

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

และ

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

ทำสิ่งเดียวกัน

ถ้าฉันเพิ่มเครื่องหมายสแลชที่สามมันก็มีผลลัพธ์เหมือนกัน แต่เมื่อฉันเพิ่มเครื่องหมายสแลชที่สี่มันไม่ทำงานอีกต่อไป สิ่งนี้เกี่ยวข้องกับคำถามจากการสอบแบบเก่าสำหรับชั้นเรียน มันถามว่าคนที่มีแบ็กสแลชสองตัวอันหนึ่งจะทำงานเพื่อเอาท์พุทบรรทัดด้วย "เช่น" ฉันคิดว่ามันไม่ทำงาน แต่ฉันพยายามทำให้แน่ใจและมันก็ทำเช่นนั้น คำอธิบายคืออะไร


ฉันคิดว่าทุบตีจะใช้เวลา\\\.และให้ grep \.แต่ไม่ คำถามที่ดี

คำตอบ:


9

ก่อนอื่นให้สังเกตว่าเครื่องหมายทับเดียวตรงมากเกินไป

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

เท่าที่เกี่ยวข้องกับทุบตีเป็นระยะเวลาหนีเหมือนช่วงเวลา ทุบตีผ่านระยะเวลาที่จะ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 สอง slash และ a period grep เห็นทั้งสองทับและระยะเวลาและลดสองทับที่เดียวที่แท้จริงเฉือน ถ้าอินพุตมีเครื่องหมายสแลชตามตัวอักษรตามด้วยอักขระใด ๆ ไม่มีการจับคู่

เพื่อแสดงให้เห็นว่าสุดท้ายโปรดจำไว้ว่าภายในเครื่องหมายคำพูดเดี่ยวอักขระทั้งหมดเป็นตัวอักษร ดังนั้นเมื่อกำหนดบรรทัดอินพุตสามบรรทัดต่อไปนี้คำสั่ง grep จะจับคู่เฉพาะบนบรรทัดที่มีตัวอักษรสแลชในอินพุต:

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

สรุปพฤติกรรมของ Bash

สำหรับ Bash กฎนั้นคือ

  • สองสแลชจะลดลงเป็นสแลชเดียว

  • เครื่องหมายทับหน้าอักขระปกติเช่นเครื่องหมายจุดเป็นเพียงอักขระปกติ (จุด)

ดังนั้น:

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

มีวิธีง่าย ๆ ในการหลีกเลี่ยงความสับสนทั้งหมดนี้: บนบรรทัดคำสั่ง Bash ควรมีการวางนิพจน์ทั่วไปไว้ในเครื่องหมายคำพูดเดี่ยว ภายในเครื่องหมายคำพูดเดี่ยว Bash จะทิ้งทุกสิ่งไว้คนเดียว

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

คำถาม: ใช้แบ็กสแลชสองครั้งสำหรับทุบตีเพื่อดูว่าเป็นแบ็กสแลช (อันหนึ่งคือลำดับการหลบหลีกส่วนอีกอันเป็นแบ็คสแลชตามตัวอักษร) ดังนั้นเมื่อมี 3 จะทุบตีถือว่าพลัดหลงที่สามเป็นลำดับหนีเช่นกัน? เนื่องจากมันไม่ได้หนีอะไรเลย
Franz Kafka

@DanielAmaya คนที่สามถือว่าเป็นตัวละครที่ตามมา ในกรณีของเราตัวละครนั้นเป็นช่วงเวลาและสำหรับทุบตี (ไม่เหมือนกับ grep) ช่วงเวลาที่หลบหนีเป็นเพียงช่วงเวลาธรรมดา bash จะผ่านช่วงเวลาธรรมดาไปที่ grep
John1024

@DanielAmaya ดูคำตอบที่ปรับปรุงแล้วสำหรับechoคำสั่งที่แสดงให้เห็นว่า bash ทำอะไรได้บ้างในกรณีเหล่านี้
John1024

2
@DanielAmaya ในทั้งสองกรณี bash ลดเครื่องหมายทับสองอันแรกเป็นเครื่องหมายทับเดียว สิ่งที่ยังคงเป็นหรือ\. .สำหรับทุบตีทั้งสองจะเหมือนกัน: พวกเขาเทียบเท่ากับช่วงเวลาธรรมดา ดังนั้นโดยรวมแล้วสิ่งที่ bash ส่งไปยัง grep เหมือนกันสำหรับทั้งสอง: slash เดียวตามด้วยจุด
John1024

1
การเพิ่มเล็ก ๆ น้อย ๆechoไม่ใช่วิธีที่เชื่อถือได้มากในการทดสอบ regexp เนื่องจากมีการใช้งานโปรแกรมนี้จำนวนมาก ยกตัวอย่างเช่นภายใต้ zsh ของฉัน (ในตัวก้อง) echo \. \\. \\\. \\\\. \\\\\.ให้. \. \. \. \.แต่ผลตอบแทน/bin/echo \. \\. \\\. \\\\. \\\\\. . \. \. \\. \\.สิ่งที่ชอบprintf "%s" ...น่าจะเป็นวิธีที่ดีกว่า
jimmij

4

ผลลัพธ์จะเหมือนกันสำหรับสตริงของคุณเท่านั้น แต่โดยทั่วไปนิพจน์ทั่วไปเหล่านั้นจะทำสิ่งที่แตกต่างกัน ลองปรับเปลี่ยนตัวอย่างของคุณเล็กน้อยโดยการเพิ่มรูปแบบที่สองe,g,(กับ comas), สามe\.g\.(จุด), ที่สี่e\,g\,(comas) และ-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

เมื่อคุณทำ a grep e\.g\., เชลล์ใช้แบ็กสแลช, ดังนั้นคุณกำลังทำ a grep e.g., ซึ่งตรงกัน เมื่อคุณทำ a grep e\\.g\\.เชลล์จะใช้สแลชอีกครั้งและตอนนี้คุณกำลังทำ a grep e\.\g.ซึ่งจะจับคู่อีกครั้ง \\ตอนนี้ทับขวาจะมีลักษณะเปลือกเช่น ดังนั้นเมื่อคุณมี\\คนแรกคือลำดับการหลบหนีที่สองคือแบ็กสแลชตามตัวอักษร เมื่อคุณทำgrep e\\\.g\\\.ก็ยังคงสิ้นสุดขึ้นเป็นgrep e\.\g.เพราะมีไม่ได้เป็นลำดับหนี ( \) ก่อนที่จะเป็นครั้งแรกที่จะทำให้มันเป็นตัวอักษร\ \โปรดจำไว้ว่า \ เป็นแบ็กสแลชดังนั้นจึง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

Regex เป็น grep

\bashเป็นตัวหนีที่มีความหมายเช่นเดียวกับใน .เป็นตัวละครพิเศษที่แสดงให้เห็นถึงสิ่งที่เกิดขึ้นเพียงครั้งเดียวของตัวอักษรใดดู: POSIX regex , GNU grep regex ตัวอย่างของนิพจน์ 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.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.