วิธีการส่งตัวแปรที่มีเครื่องหมายทับไปยัง sed


106

คุณส่งตัวแปรที่มีเครื่องหมายทับเป็นรูปแบบไปยังsedอย่างไร?

ตัวอย่างเช่นหากฉันมีตัวแปรต่อไปนี้:

var="/Users/Documents/name/file"

ฉันต้องการส่งต่อให้sedเป็นดังนี้:

sed "s/$var/replace/g" "$file"

อย่างไรก็ตามฉันได้รับข้อผิดพลาด ฉันจะหลีกเลี่ยงปัญหาได้อย่างไร

คำตอบ:


203

ใช้ตัวคั่น regex ทางเลือกเพื่อsedให้คุณใช้ตัวคั่น ( รวมถึงอักขระควบคุม ):

sed "s~$var~replace~g" $file

2
ไม่ทำงาน ฉันลอง sed -i "s ~ blah ~ $ var ~ g file" และรับ: "sed -e expression # 1, char 70: unterminated` s 'command " ฉันได้ยืนยันแล้วว่าผู้ร้ายคือเครื่องหมายทับใน $ var รูปแบบต่างๆของวิธีแก้ปัญหานี้มีอยู่ทั่วไปและไม่มีวิธีใดที่ใช้งานได้ ได้รับการอัปเดตเมื่อเร็ว ๆ นี้? ฉันใช้ v4.2.2 บน Ubuntu 14.04.4 LTS
Paul Ericson

ขึ้นอยู่กับมูลค่าของ$var
anubhava

1
ใช้ไม่ได้กับ ~ ตามที่ @PaulEricson สังเกต แต่ยังใช้งานได้กับ |
Kenny Ho

1
ดูเหมือนว่าไม่จำเป็นต้องใช้ "~" (หลัง g) สุดท้ายอย่างน้อยสำหรับ AWS Amazon Linux (ใช้ CentOS)
SaúlMartínez Vidals

1
คุณบันทึกวันของฉัน
user7131571

65

คำตอบ bash ที่แท้จริง: ใช้การขยายพารามิเตอร์เพื่อแบ็กสแลช - หลีกเลี่ยงเครื่องหมายทับในตัวแปร:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file

1
นี่เป็นวิธีที่ดีเพราะคุณมักไม่รู้ว่าตัวอักษรประกอบด้วยตัวแปรอะไร ดังนั้นคุณจึงสามารถหลีกเลี่ยงตัวคั่น sed ได้ตลอดเวลาหากมีข้อสงสัยตัวอย่างเช่น sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L

สวย. ทำให้สคริปต์การเปลี่ยนชื่อของฉันสะอาดขึ้นมาก
Cloud

ได้เรียนรู้สิ่งที่สวยงามในวันนี้ขอบคุณ ควรเป็นคำตอบที่ได้รับการยอมรับ
Steve Kehlet

8
${parameter/pattern/string}ความชัดเจนบางอย่างเกี่ยวกับรูปแบบการใช้ที่นี่: ดังนั้นในกรณีนี้พารามิเตอร์เป็นvarรูปแบบที่มีและสตริง/\/ \\/รูปแบบทั้งหมดจะถูกแทนที่เนื่องจากรูปแบบเริ่มต้นด้วย/.
Josh

1
@josh ดังนั้นเครื่องหมายทับของรูปแบบจึงไม่ได้เป็นส่วนหนึ่งของรูปแบบที่จะแทนที่
glenn jackman

33

อีกวิธีหนึ่งในการทำเช่นนี้แม้ว่าจะน่าเกลียดกว่าคำตอบของ anubhava แต่ก็คือการหลีกเลี่ยงแบ็กสแลชทั้งหมดvarโดยใช้sedคำสั่งอื่น:

var=$(echo "$var" | sed 's/\//\\\//g')

จากนั้นสิ่งนี้จะได้ผล:

sed "s/$var/replace/g" $file

12

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

var="/Users/Documents/name/file"

คุณสามารถใช้อักขระ octothorpe ที่เหมาะสมกับโอกาส (หรืออักขระอื่น ๆ ที่ไม่ใช่/เพื่อความสะดวกในการใช้งาน)

sed "s#$var#replace#g" 

หรือ

sed 's#$'$var'#replace#g'

เหมาะเมื่อตัวแปรไม่มีช่องว่าง

หรือ

sed 's#$"'$var'"#replace#g'

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


ไม่ทำงาน ฉันลอง sed -i "s ~ blah ~ $ var ~ g file" และรับ: "sed -e expression # 1, char 70: unterminated` s 'command " ฉันยืนยันแล้วว่าผู้ร้ายคือเครื่องหมายทับใน $ var ความหลากหลายของวิธีแก้ปัญหานี้มีอยู่ทั่วทุกแห่งและไม่มีวิธีใดที่ใช้งานได้ ได้รับการอัปเดตเมื่อเร็ว ๆ นี้? ฉันใช้ v4.2.2 บน Ubuntu 14.04.4 LTS
Paul Ericson

@PaulEricson ฉันไม่สามารถ repro สิ่งนี้บน Ubuntu ใด ๆ ที่มีให้ฉัน หาก$varมี~คุณจะต้องเลือกตัวคั่นอื่นอย่างชัดเจน ข้อผิดพลาด "ไม่สิ้นสุด" ดูเหมือนคุณ$varมีการขึ้นบรรทัดใหม่ คุณอาจสามารถแก้ไขได้โดยใช้เครื่องหมายแบ็กสแลชหลีกเลี่ยงค่าขึ้นบรรทัดใหม่ทุกครั้ง แต่ฉันไม่คิดว่านี่เป็นแบบพกพาทั้งหมด นี่คือโดยมากและไม่เกี่ยวข้องกับปัญหาของเครื่องหมายทับในค่า
tripleee

@PaulEricson โอ้และการอ้างอิงของคุณไม่ถูกต้องคุณไม่ควรมีชื่อไฟล์ในเครื่องหมายคำพูด sed -i "s~blah~$var~g" fileด้วยเครื่องหมายคำพูดรอบsedสคริปต์จริงเท่านั้น
tripleee

5

นี่เป็นคำถามเก่า แต่ไม่มีคำตอบใดที่จะกล่าวถึงการดำเนินการนอกเหนือจากs/from/to/รายละเอียดมากนัก

รูปแบบทั่วไปของsedคำสั่งคือ

*address* *action*

โดยที่อยู่อาจเป็นช่วงนิพจน์ทั่วไปหรือช่วงหมายเลขบรรทัด (หรือว่างซึ่งในกรณีนี้การดำเนินการจะถูกนำไปใช้กับทุกบรรทัดอินพุต) ตัวอย่างเช่น

sed '1,4d' file

จะลบบรรทัดที่ 1 ถึง 4 (ที่อยู่คือช่วงหมายเลขบรรทัด1,4และการดำเนินการคือdคำสั่งลบ); และ

sed '/ick/,$s/foo/bar/' file

จะแทนที่การเกิดขึ้นครั้งแรกfooด้วยbarในบรรทัดใด ๆ ระหว่างการจับคู่ครั้งแรกใน regex ickและจุดสิ้นสุดของไฟล์ (ที่อยู่คือช่วง/ick/,$และการดำเนินการคือsคำสั่งแทนที่s/foo/bar/ )

ในบริบทนี้หากickมาจากตัวแปรคุณสามารถทำได้

sed "/$variable/,\$s/foo/bar/"

(สังเกตการใช้เครื่องหมายคำพูดคู่แทนคำพูดเดี่ยวเพื่อให้เชลล์สามารถสอดแทรกตัวแปรและความจำเป็นในการอ้างเครื่องหมายดอลลาร์ตามตัวอักษรภายในเครื่องหมายคำพูดคู่) แต่ถ้าตัวแปรมีเครื่องหมายทับคุณจะได้รับข้อผิดพลาดทางไวยากรณ์ (เชลล์ขยายตัวแปรจากนั้นส่งสตริงผลลัพธ์ไปยังsed; ดังนั้นsedเห็นเฉพาะข้อความตามตัวอักษร - ไม่มีแนวคิดเกี่ยวกับตัวแปรของเชลล์)

วิธีแก้คือการใช้ตัวคั่นอื่น (ซึ่งแน่นอนว่าคุณต้องสามารถใช้อักขระที่ไม่สามารถเกิดขึ้นในค่าของตัวแปรได้) แต่ไม่เหมือนในs%foo%bar%กรณีนี้คุณต้องใช้แบ็กสแลชก่อนตัวคั่นหากคุณต้องการใช้ตัวอื่น ตัวคั่นมากกว่าค่าเริ่มต้น/:

sed "\\%$variable%,\$s/foo/bar/" file

(ภายในเครื่องหมายคำพูดเดียวแบ็กสแลชเดียวก็เพียงพอแล้ว) หรือคุณสามารถแยกทุกเครื่องหมายทับในค่า ไวยากรณ์เฉพาะนี้คือ Bash เท่านั้น:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

หรือถ้าคุณใช้เปลือกอื่นให้ลอง

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

เพื่อความชัดเจนหาก$variableมีสตริง1/2คำสั่งข้างต้นจะเทียบเท่ากับ

sed '\%1/2%,$s/foo/bar/' file

ในกรณีแรกและ

sed '/1\/2/,$s/foo/bar/' file

ในวินาที


4

ใช้ Perl โดยที่ตัวแปรเป็นพลเมืองชั้นหนึ่งไม่ใช่แค่ขยายมาโคร:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p อ่านบรรทัดอินพุตทีละบรรทัดและพิมพ์บรรทัดหลังจากการประมวลผล
  • \Qใส่เครื่องหมายคำพูด metacharacters ทั้งหมดในสตริงต่อไปนี้ (ไม่จำเป็นสำหรับค่าที่แสดงที่นี่ แต่จำเป็นหากค่าที่มีอยู่[หรือค่าอื่น ๆ เป็นพิเศษสำหรับ expresisons ปกติ)

คำตอบเดียวที่เป็นประโยชน์โดยทั่วไปสำหรับคำถาม "การใช้ตัวแปรในนิพจน์ sed" หลายสิบข้อใน Stack Exchange อย่างอื่นคือ "หลีกหนีสิ่งที่พิเศษไปยัง sed" ได้อย่างมีประสิทธิภาพซึ่งในทางปฏิบัตินั้นไม่มีประโยชน์เนื่องจากความแปรปรวนของตัวแปรและของ sed
Heath Raftery
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.