วิธีการตรวจสอบให้แน่ใจว่าสตริงที่ถูกสอดแทรกเข้าไปใน `การแทนที่ 'sed` จะหนี metachars ทั้งหมด


21

ฉันมีสคริปต์ที่อ่านข้อความกระแสและสร้างไฟล์คำสั่ง sed sed -fว่าจะดำเนินการต่อมาเป็น คำสั่ง sed ที่สร้างขึ้นมีลักษณะดังนี้:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

สมมติว่าสคริปต์ที่สร้างsedคำสั่งมีลักษณะดังนี้:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

ฉันจะปรับปรุงสคริปต์เพื่อให้มั่นใจว่าอักขระเมตาของ regex ทั้งหมดในcidสตริงได้รับการยกเว้นและแก้ไขอย่างถูกต้องหรือไม่

คำตอบ:


24

ในการหลีกเลี่ยงตัวแปรที่จะใช้ทางด้านซ้ายและด้านขวาของsคำสั่งในsed(ที่นี่$lhsและ$rhsตามลำดับ) คุณต้องทำดังนี้:

escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\\&:g;$!s/$/\\/')

sed "s/$escaped_lhs/$escaped_rhs/"

โปรดทราบว่า$lhsไม่สามารถมีอักขระขึ้นบรรทัดใหม่

นั่นคือบน LHS ให้หลีกเลี่ยงตัวดำเนินการ regexp ทั้งหมด ( ][.^$*) ตัวละครหนีเอง ( \) และตัวคั่น ( /)

บน RHS คุณจะต้องยกเว้น&ตัวคั่นเครื่องหมายแบ็กสแลชและอักขระขึ้นบรรทัดใหม่ (ซึ่งคุณทำโดยการแทรกแบ็กสแลชที่ท้ายแต่ละบรรทัดยกเว้นตัวสุดท้าย ( $!s/$/\\/))

ที่ถือว่าคุณใช้/เป็นตัวคั่นในsed sคำสั่งของคุณและคุณไม่ได้เปิดใช้งานExtended REsด้วย-r(GNU sed/ ssed/ ast/ busybox sed) หรือ-E(BSDs, astGNU ล่าสุด, busybox ล่าสุด) หรือPCREs ที่มี-R( ssed) หรือRE Re Augmentedกับ-A/ -X( ast) ทั้งหมดมีตัวดำเนินการ RE พิเศษ

กฎพื้นฐานบางประการเมื่อจัดการกับข้อมูลที่กำหนดเอง:

  • อย่าใช้ echo
  • พูดตัวแปรของคุณ
  • พิจารณาผลกระทบของโลแคล (โดยเฉพาะชุดอักขระ: เป็นสิ่งสำคัญที่คำสั่งescaping sedจะทำงานในโลแคลเดียวกันกับsedคำสั่งโดยใช้สตริงที่ใช้Escape (และด้วยsedคำสั่งเดียวกัน) เป็นต้น)
  • อย่าลืมเกี่ยวกับอักขระขึ้นบรรทัดใหม่ (ที่นี่คุณอาจต้องการตรวจสอบว่า$lhsมีและดำเนินการใด ๆ )

ตัวเลือกอื่นคือการใช้perlแทนsedและส่งผ่านสตริงในสภาพแวดล้อมและใช้ตัวดำเนินการ\Q/ \E perlregexp สำหรับการรับสตริงอย่างแท้จริง:

A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

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


จะทำอย่างไรถ้าฉันต้องการหนีเครื่องหมายคำพูดคู่
Menon

@Menon, เครื่องหมายคำพูดคู่ไม่ได้เป็นพิเศษsedคุณไม่จำเป็นต้องหลบหนี
Stéphane Chazelas

สิ่งนี้ไม่สามารถใช้สำหรับการจับคู่รูปแบบโดยใช้ wildcard ได้หรือไม่
Menon

@Menon ไม่ตรงกับรูปแบบสัญลักษณ์แทนเช่นเดียวกับfind's -nameแตกต่างจากนิพจน์ทั่วไป ที่นั่นคุณเพียง แต่ต้องหลบหนี?, *แบ็กสแลชและ[
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.