ฉันต้องหลบตัวละครอะไรเมื่อใช้ sed ในสคริปต์ sh?


248

ใช้สคริปต์ต่อไปนี้:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

หากฉันพยายามเรียกใช้สิ่งนี้ในsh( dashที่นี่) มันจะล้มเหลวเนื่องจากวงเล็บซึ่งต้องหลีกหนี แต่ฉันไม่จำเป็นต้องหลบหลีกแบ็กสแลชตัวเอง (ระหว่างออคเต็ตหรือใน\sหรือ\1) กฎนี่คืออะไร สิ่งที่เกี่ยวกับเมื่อฉันจำเป็นต้องใช้{...}หรือ[...]? มีรายการสิ่งที่ฉันทำและไม่จำเป็นต้องหลบหนีหรือไม่?


1
นี่คือฟังก์ชั่นทุบตีสำหรับการแปลงเส้นทางสำหรับใช้กับ SED:function sedPath { path=$((echo $1|sed -r 's/([\$\.\*\/\[\\^])/\\\1/g'|sed 's/[]]/\[]]/g')>&1) } #Escape path for use with sed
2428118


Dura lex, sed sed
Nemo

คำตอบ:


281

ที่นี่มีการตีความสองระดับ: เปลือกและ sed

ในเชลล์ทุกอย่างระหว่างเครื่องหมายคำพูดเดียวจะถูกตีความอย่างแท้จริงยกเว้นเครื่องหมายคำพูดเดียว คุณสามารถมีเครื่องหมายคำพูดเดี่ยวระหว่างเครื่องหมายคำพูดเดี่ยวได้อย่างมีประสิทธิภาพโดยการเขียน'\''(ปิดเครื่องหมายคำพูดเดี่ยวเครื่องหมายคำพูดเดียวหนึ่งตัวและเครื่องหมายคำพูดเดี่ยวเปิด)

sed ใช้การแสดงออกปกติพื้นฐาน ใน BRE เพื่อที่จะให้พวกเขาได้รับการปฏิบัติอย่างแท้จริงตัวละคร$.*[\^จะต้องได้รับการอ้างอิงโดยนำหน้าด้วยแบ็กสแลชยกเว้นชุดอักขระภายใน ( […]) ตัวอักษรตัวเลขและ(){}+?|ต้องไม่ยกมา (คุณสามารถออกไปด้วยการอ้างอิงบางส่วนในการใช้งานบางอย่าง) ลำดับ\(, \), \nและในการใช้งานบาง\{, \}, \+, \?, \|และอื่น ๆ ทับขวา + Alphanumerics มีความหมายพิเศษ คุณสามารถออกไปโดยไม่พูดถึง$^ในบางตำแหน่งในการนำไปใช้งานบางอย่าง

นอกจากนี้คุณต้องมีแบ็กสแลชก่อนที่/จะให้ปรากฏใน regex นอกของนิพจน์วงเล็บเหลี่ยม คุณสามารถเลือกตัวละครอื่นเป็นตัวคั่นโดยการเขียนเช่นs~/dir~/replacement~หรือ\~/dir~p; คุณจะต้องมีแบ็กสแลชต่อหน้าตัวคั่นหากคุณต้องการรวมไว้ใน BRE หากคุณเลือกตัวละครที่มีความหมายพิเศษใน BRE และคุณต้องการรวมมันอย่างแท้จริงคุณจะต้องมีแบ็กสแลชสามตัว ฉันไม่แนะนำสิ่งนี้เนื่องจากมันอาจมีพฤติกรรมแตกต่างกันในการใช้งานบางอย่าง

โดยสรุปสำหรับsed 's/…/…/':

  • เขียน regex ระหว่างเครื่องหมายคำพูดเดี่ยว
  • ใช้'\''เพื่อจบด้วยการเสนอราคาเดียวใน regex
  • ใส่แบ็กสแลชก่อน$.*/[\]^และเฉพาะอักขระเหล่านั้นเท่านั้น (แต่ไม่ใช่ภายในนิพจน์วงเล็บเหลี่ยม) (โดยทางเทคนิคแล้วคุณไม่ควรใส่แบ็กสแลชไว้ก่อนหน้านี้]แต่ฉันไม่รู้ว่ามีการนำไปใช้ที่ปฏิบัติ]และ\]แตกต่างจากนิพจน์วงเล็บเหลี่ยม)
  • ภายในนิพจน์วงเล็บเหลี่ยมเพื่อ-ให้ได้รับการปฏิบัติอย่างแท้จริงให้แน่ใจว่ามันเป็นครั้งแรกหรือครั้งสุดท้าย ( [abc-]หรือ[-abc]ไม่[a-bc])
  • ภายในแสดงออกวงเล็บสำหรับ^ที่จะได้รับการปฏิบัติอย่างแท้จริงให้แน่ใจว่ามันไม่ได้เป็นครั้งแรก (ใช้[abc^]ไม่ได้[^abc])
  • หากต้องการรวม]ไว้ในรายการของอักขระที่จับคู่โดยนิพจน์วงเล็บปีกกาให้ตั้งค่าเป็นอักขระตัวแรก (หรือก่อนหลัง^สำหรับเซตที่ถูกทำให้ว่าง): []abc]หรือ[^]abc](ไม่ใช่[abc]]หรือ[abc\]] )

ในข้อความแทนที่:

  • &และ\จำเป็นต้องมีเครื่องหมายคำนำหน้าโดย backslash เช่นเดียวกับตัวคั่น (ปกติ/) และบรรทัดใหม่
  • \ตามด้วยตัวเลขมีความหมายพิเศษ \ตามด้วยตัวอักษรมีความหมายพิเศษ (อักขระพิเศษ) ในการใช้งานบางอย่างและ\ตามด้วยตัวอักษรอื่น ๆ หมายถึง\cหรือcขึ้นอยู่กับการใช้งาน
  • ด้วยเครื่องหมายคำพูดเดี่ยวรอบ ๆ อาร์กิวเมนต์ ( sed 's/…/…/') ให้ใช้'\''เพื่อใส่เครื่องหมายคำพูดเดี่ยวในข้อความแทนที่

หากข้อความ regex หรือการแทนที่มาจากตัวแปรเชลล์โปรดจำไว้ว่า

  • regex เป็น BRE ไม่ใช่สตริงตัวอักษร
  • ใน regex จะต้องมีการขึ้นบรรทัดใหม่เป็น\n(ซึ่งจะไม่ตรงกันเว้นแต่คุณจะมีsedรหัสอื่น ๆ ที่เพิ่มอักขระขึ้นบรรทัดใหม่ลงในพื้นที่รูปแบบ) แต่โปรดทราบว่าจะไม่ทำงานภายในนิพจน์วงเล็บเหลี่ยมด้วยsedการใช้งานบางอย่าง
  • ในข้อความทดแทน&, \และการขึ้นบรรทัดใหม่จะต้องมีการอ้าง
  • ตัวคั่นต้องถูกยกมา (แต่ไม่ใช่ภายในนิพจน์วงเล็บเหลี่ยม)
  • sed -e "s/$BRE/$REPL/"ใช้คำพูดสองสำหรับการแก้ไข:

การหลีกเลี่ยงอักขระตัวแทนจริง (*) คุณสามารถใช้เครื่องหมายแบ็กสแลชคู่ ( \\*) ตัวอย่าง:echo "***NEW***" | sed /\\*\\*\\*NEW\\*\\*\\*/s/^/#/
danger89

43

ปัญหาที่คุณประสบไม่ได้เกิดจากการสอดแทรกของเชลล์และหนีออกมา - เพราะคุณกำลังพยายามใช้ไวยากรณ์นิพจน์ปกติเพิ่มเติมโดยไม่ผ่านตัวเลือก-rหรือ--regexp-extendedตัวเลือก

เปลี่ยนสาย sed ของคุณจาก

sed 's/(127\.0\.1\.1)\s/\1/' [some file]

ไปยัง

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]

และมันจะทำงานตามที่ฉันเชื่อว่าคุณตั้งใจ

โดยค่าเริ่มต้น sed ใช้การใช้การแสดงออกปกติพื้นฐาน (คิดว่าสไตล์ grep) ซึ่งจะต้องใช้ไวยากรณ์ต่อไปนี้:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]

ฉันมีปัญหานี้อีกครั้งและลืมที่จะเลื่อนลงเพื่อค้นหาวิธีแก้ไขที่ฉันโหวตขึ้นครั้งล่าสุด ขอบคุณอีกครั้ง.
isaaclw

ขอบคุณมาก. การเพิ่ม-rเป็นตัวเลือกเป็นสิ่งที่จำเป็นในกรณีของฉัน
HelloGoodbye

15

หากคุณไม่ต้องการสอดแทรกตัวแปรเชลล์ลงในนิพจน์ sed ให้ใช้เครื่องหมายอัญประกาศเดี่ยวสำหรับนิพจน์ทั้งหมดเพราะจะทำให้ทุกอย่างระหว่างพวกมันถูกตีความตามที่เป็นอยู่รวมถึงแบ็กสแลช

ดังนั้นหากคุณต้องการให้ sed s/\(127\.0\.1\.1\)\s/\1/ใส่เครื่องหมายอัญประกาศล้อมรอบมันและเชลล์จะไม่แตะเครื่องหมายวงเล็บหรือแบ็กสแลช หากคุณต้องการสอดแทรกตัวแปรเชลล์ให้ใส่เฉพาะส่วนนั้นในเครื่องหมายคำพูดคู่ เช่น

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

การทำเช่นนี้จะช่วยให้คุณทราบถึงปัญหาในการจดจำตัวบ่งชี้เชลล์ตัวใดที่ไม่สามารถหลีกเลี่ยงได้ด้วยเครื่องหมายคำพูดคู่


ฉันต้องการsedเห็นs/(127\.0\.1\.1)/...แต่การวางไว้ในเชลล์สคริปต์ตาม - ไม่ทำงาน สิ่งที่คุณพูดเกี่ยวกับเปลือกที่ไม่ได้สัมผัสกับวงเล็บดูเหมือนผิด ฉันได้แก้ไขคำถามของฉันเพื่ออธิบายอย่างละเอียด
Detly

3
เปลือกไม่ได้สัมผัสกับวงเล็บ คุณต้องการ backslases เพราะsedต้องการเห็นมัน sed 's/(127\.0\.1\.1)/IP \1/'ล้มเหลวเนื่องจาก sed ต้องการที่จะเห็น\(และ\)ไวยากรณ์กลุ่มไม่ได้และ( )
Kyle Jones

facepalmมันไม่ได้อยู่ใน man page แต่มันอยู่ในคู่มือออนไลน์ที่ฉันพบ เป็นเรื่องปกติสำหรับ regex หรือไม่เพราะฉันไม่เคยใช้มันในไลบรารี regex (เช่น Python)?
Detly

3
สำหรับคำสั่ง Unix แบบดั้งเดิมมีนิพจน์ทั่วไปพื้นฐานและนิพจน์ทั่วไปที่ขยายเพิ่ม รายละเอียด . sed ใช้นิพจน์ปกติพื้นฐานดังนั้นแบ็กสแลชจึงจำเป็นสำหรับไวยากรณ์กลุ่ม Perl และ Python ก้าวล้ำกว่าการแสดงออกปกติ ในขณะที่ฉันเดินไปมาฉันพบแผนภูมิที่ให้ข้อมูลอย่างมากซึ่งแสดงให้เห็นถึงสิ่งที่หนามสับสนที่เราคิดในใจเมื่อเราพูดว่า "การแสดงออกปกติ"
Kyle Jones

1
ฉันจะเพิ่มว่าตัวละครเดียวที่ไม่สามารถใช้ภายในเครื่องหมายคำพูดเดียวคือเครื่องหมายคำพูดเดี่ยว
enzotib
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.