วิธีการแทนที่สตริงที่มีเครื่องหมายทับด้วย sed?


147

ฉันมีโครงการ Visual Studio ซึ่งพัฒนาขึ้นในเครื่อง ไฟล์รหัสจะต้องมีการปรับใช้กับเซิร์ฟเวอร์ระยะไกล ปัญหาเดียวคือ URLsthey บรรจุซึ่งมีการกำหนดค่าตายตัว

โครงการมี URL เช่น? page = หนึ่ง สำหรับการเชื่อมโยงที่จะถูกต้องบนเซิร์ฟเวอร์นั้นจะต้องได้/ หน้า / หนึ่ง

ฉันตัดสินใจแทนที่ URLS ทั้งหมดใน codefiles ด้วย sed ก่อนการปรับใช้ แต่ฉันติดอยู่ที่เครื่องหมายทับ

ฉันรู้ว่านี่ไม่ใช่วิธีแก้ปัญหาที่สวย แต่มันง่ายที่จะช่วยฉันประหยัดเวลาได้มาก จำนวนสตริงทั้งหมดที่ฉันต้องแทนที่น้อยกว่า 10 จำนวนไฟล์ทั้งหมดที่ต้องถูกตรวจสอบคือ ~ 30

ตัวอย่างที่อธิบายสถานการณ์ของฉันอยู่ด้านล่าง:

คำสั่งที่ฉันใช้:

sed -f replace.txt < a.txt > b.txt

replace.txt ซึ่งมีสตริงทั้งหมด:

s/?page=one&/pageone/g
s/?page=two&/pagetwo/g
s/?page=three&/pagethree/g

a.txt:

?page=one&
?page=two&
?page=three&

เนื้อหาของ b.txt หลังจากฉันเรียกใช้คำสั่ง sed ของฉัน:

pageone
pagetwo
pagethree

สิ่งที่ฉันต้องการให้ b.txt มี:

/page/one
/page/two
/page/three

1
อาจเป็นไปได้ซ้ำซ้อนกับการค้นหาและแทนที่สตริงที่มี /
Damien MATHIEU

คำตอบ:


274

วิธีที่ง่ายที่สุดคือการใช้ตัวคั่นอื่นในบรรทัดการค้นหา / แทนที่ของคุณเช่น:

s:?page=one&:pageone:g

คุณสามารถใช้อักขระใด ๆ เป็นตัวคั่นที่ไม่ได้เป็นส่วนหนึ่งของสตริงใดก็ได้ หรือคุณสามารถหลบหนีด้วยแบ็กสแลช:

s/\//foo/

ซึ่งจะเข้ามาแทนที่ด้วย/ fooคุณต้องการใช้เครื่องหมายแบคสแลชที่ Escape ในกรณีที่คุณไม่ทราบว่าอักขระใดที่อาจเกิดขึ้นในสตริงการแทนที่ (เช่นเป็นตัวแปรเชลล์ตัวอย่าง)


1
> หรือคุณสามารถหลบหนีด้วยแบ็กสแลช ตัวอย่างของสิ่งนั้นจะมีประโยชน์มากกว่าเพราะคุณไม่รู้ว่าตัวละครในสตริงจะเลือกอะไรที่แตกต่างออกไปได้เสมอ เช่นนี้: echo / | sed s / \ // a / g ไม่ทำงาน: sed: -e expression # 1, อักขระ 5: ไม่รู้จักตัวเลือก `s '
Max ฝีพาย

1
คุณจะเพิ่มได้ไหม? ขอบคุณ :) ฉันพบว่าการใช้เครื่องหมายอัญประกาศล้อมรอบดูเหมือนจะใช้ได้: echo / | sed "s / \ // a / g"
Max Waterman

@MaxWaterman เป็นขั้นตอนการปฏิบัติการมาตรฐานเมื่อใช้sedคำสั่ง regex ในเครื่องหมายคำพูดคู่ ฉันไม่ได้ใช้พวกเขาในคำตอบเพราะฉันไม่ได้แสดงsedบรรทัดคำสั่งทั้งหมดแต่เพียงแค่sedสตริงคำสั่ง regex ตามที่ OP ทำ หากคุณใส่ไว้ในไฟล์อย่างที่ OP ทำคุณไม่จำเป็นต้องใส่เครื่องหมายคำพูด
lurker

ใช่ยุติธรรมพอ (อาจจะพูดถึงก็ได้) ตัวอย่างนั้นช่วย ฉันพบว่าบางครั้งฉันจำเป็นต้องใส่แบ็กสแลชจำนวนมากและบางครั้งก็ทำให้เกิดความสับสน เช่น -e "s / '/ \\\\\\\ & / g" ฉันคิดว่าข้อความผิดแม้ว่า: "จะแทนที่ \ with foo" - ควรเป็น "ซึ่งจะแทนที่ / ด้วย foo" ไม่?
Max Waterman

@ MaxWaterman ขอบคุณสำหรับการจับบน \ กับ / ซ่อมมัน. หากคุณมีsedคำสั่งในเชลล์สคริปต์อาจจำเป็นต้องใช้แบ็กสแลชเพิ่มขึ้นอีก
lurker

105

sคำสั่งสามารถใช้ตัวอักษรใด ๆ เป็นตัวคั่น; สิ่งที่ตัวละครมาหลังจากการsใช้งาน #ฉันถูกนำขึ้นเพื่อการใช้งาน ชอบมาก

s#?page=one&#/page/one#g

5
man page สำหรับ BSD sed บน OS X กล่าวถึงคำสั่งs : แทนที่สตริงการแทนที่สำหรับอินสแตนซ์แรกของการแสดงออกปกติในพื้นที่รูปแบบ อักขระอื่นที่ไม่ใช่แบ็กสแลชหรือขึ้นบรรทัดใหม่สามารถใช้แทนเครื่องหมายทับเพื่อกำหนดขอบเขต RE และการแทนที่ ฉันจะเดิมพันเงินที่หน้าคนสำหรับ GNU sed พูดอะไรบางอย่างที่คล้ายกัน
ทอมแอนเดอร์สัน

คำตอบที่ได้รับการยอมรับในปัจจุบันนั้นเป็นคำตอบเดียวกับคำตอบนี้และถูกโพสต์ไว้หนึ่งนาที
Tom Anderson

61

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


คำแนะนำอัจฉริยะเมื่อคุณต้องการแทนที่เครื่องหมายสแลช ขอบคุณ!
mbb

9

เพิ่ม \ ก่อนอักขระพิเศษ:

s/\?page=one&/page\/one\//g

เป็นต้น


4
ฉันอาจจะพลาดอะไรบางอย่างไป แต่ฉันได้ลองแล้วก็ใช้ไม่ได้ ดูเหมือนจะเป็นสิ่งที่ชัดเจนที่จะลอง แต่สมมติว่าฉันพูดถูกและมันใช้งานไม่ได้จริง ๆ ทำไมต้องโพสต์มัน
codenoob

4
@codenoob (และใครก็ตามที่มาที่นี่) - 's' ที่จุดเริ่มต้นจะต้อง s/foo\/bar/foo_bar/จะทำงาน แต่/foo\/bar/foo_bar/จะไม่
MynockSpit

5

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

ตามที่ระบุไว้ก่อนหน้านี้ในโพสต์นี้ถ้าสตริงที่มีอยู่ในบล็อกคำสั่ง sed มีตัวคั่นที่แท้จริงที่ใช้โดย sed - แล้ว sed จะยุติลงในข้อผิดพลาดทางไวยากรณ์ ลองพิจารณาตัวอย่างต่อไปนี้:

งานนี้:

$ VALUE=12345
$ echo "MyVar=%DEF_VALUE%" | sed -e s/%DEF_VALUE%/${VALUE}/g
MyVar=12345

ช่วงเวลานี้:

$ VALUE=12345/6
$ echo "MyVar=%DEF_VALUE%" | sed -e s/%DEF_VALUE%/${VALUE}/g
sed: -e expression #1, char 21: unknown option to `s'

การแทนที่ตัวคั่นดีฟอลต์ไม่ใช่โซลูชันที่มีประสิทธิภาพในกรณีของฉันเนื่องจากฉันไม่ต้องการ จำกัด ผู้ใช้จากการป้อนอักขระเฉพาะที่ใช้โดย sed เป็นตัวคั่น (เช่น "/")

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

$ VALUE=$(echo ${VALUE} | sed -e "s#/#\\\/#g")
$ echo "MyVar=%DEF_VALUE%" | sed -e s/%DEF_VALUE%/${VALUE}/g
MyVar=12345/6

ฉันได้แปลงสิ่งนี้เป็นฟังก์ชั่นที่ใช้โดยสคริปต์ต่าง ๆ :

escapeForwardSlashes() {

     # Validate parameters
     if [ -z "$1" ]
     then
             echo -e "Error - no parameter specified!"
             return 1
     fi

     # Perform replacement
     echo ${1} | sed -e "s#/#\\\/#g"
     return 0
}

1
การนำออกไปจากคำตอบของคุณสำหรับฉันคือถ้าค่าที่คุณใช้เพื่อแทนที่ DEF_VALUE มีเครื่องหมายทับอยู่ข้างหน้าคุณต้องหลบหลีกพวกเขาด้วยแบ็กสแลช 3 อันเพื่อให้ทำงานเช่นVALUE="01\\\/01\\\/2018"
alexkb

3

บรรทัดนี้ควรใช้งานได้กับ 3 ตัวอย่างของคุณ:

sed -r 's#\?(page)=([^&]*)&#/\1/\2#g' a.txt
  • ฉันเคย-rบันทึกการหลบหนีบางอย่าง
  • บรรทัดควรเป็นแบบทั่วไปสำหรับหนึ่งในสองกรณีของคุณ คุณไม่ต้องทำย่อย 3 ครั้ง

ทดสอบด้วยตัวอย่างของคุณ (a.txt):

kent$  echo "?page=one&
?page=two&
?page=three&"|sed -r 's#\?(page)=([^&]*)&#/\1/\2#g'
/page/one
/page/two
/page/three


1

คำตอบที่ดีจากผู้ไม่ประสงค์ออกนาม \ แก้ไขปัญหาของฉันเมื่อฉันพยายามหลีกเลี่ยงคำพูดในสตริง HTML

ดังนั้นหากคุณใช้ sed เพื่อส่งคืนเท็มเพลต HTML บางตัว (บนเซิร์ฟเวอร์) ให้ใช้เครื่องหมายแบ็กสแลชสองครั้งแทนที่จะเป็นแบบเดี่ยว:

var htmlTemplate = "<div style=\\"color:green;\\"></div>";

1

sedเป็นs tream ed itorซึ่งคุณสามารถใช้|( ไพพ์ ) เพื่อส่งสตรีมมาตรฐาน (STDIN และ STDOUT โดยเฉพาะ) ผ่านsedและปรับเปลี่ยนให้เป็นแบบโปรแกรมได้ทันทีทำให้เป็นเครื่องมือที่สะดวกในประเพณีปรัชญา Unix; แต่สามารถแก้ไขไฟล์ได้โดยตรงเช่นกันโดยใช้-iพารามิเตอร์ที่กล่าวถึงด้านล่าง
พิจารณาสิ่งต่อไปนี้ :

sed -i -e 's/few/asd/g' hello.txt

s/จะใช้ในการs ubstitute การแสดงออกพบfewกับasd:

ไม่กี่คนที่กล้าหาญ


ผู้ช่วยผู้กล้า

/gย่อมาจาก "ทั่วโลก" หมายถึงการทำเช่นนี้เพื่อทั้งบรรทัด หากคุณออกจาก/g(ด้วยs/few/asd/จะต้องมีสามเครื่องหมายทับไม่ว่าจะเกิดอะไรขึ้น) และfewปรากฏสองครั้งในบรรทัดเดียวกันเฉพาะอันแรกเท่านั้นที่fewเปลี่ยนเป็นasd:

ผู้ชายไม่กี่ผู้หญิงไม่กี่คนที่กล้าหาญ


ผู้ชาย asd ผู้หญิงไม่กี่คนที่กล้าหาญ

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

สองตัวเลือก (ธง) ต่อไปนี้จะรวมกันเป็นหนึ่ง-ie:

-iตัวเลือกที่ใช้ในการแก้ไขฉันในสถานที่ไฟล์hello.txtที่ไฟล์

-eตัวเลือกบ่งชี้ว่าe xpression / คำสั่งให้ทำงานในกรณีนี้s/นี้

หมายเหตุ: สิ่งสำคัญคือคุณ-i -eต้องใช้ในการค้นหา / แทนที่ หากคุณทำเช่น-ieนั้นคุณจะสร้างสำเนาสำรองของทุกไฟล์โดยใช้ตัวอักษร 'e' ต่อท้าย


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