สลับการเกิดทั้งหมดสองสายโดยใช้ sed


13

สมมติว่าฉันมีไฟล์ที่มีการเกิดขึ้นหลายครั้งของทั้ง StringA และ StringB ฉันต้องการแทนที่การเกิดขึ้นทั้งหมดของ StringA ด้วย StringB และ (พร้อมกัน) การเกิดขึ้นทั้งหมดของ StringB ด้วย StringA

ตอนนี้ฉันกำลังทำอะไรบางอย่าง

cat file.txt | sed 's/StringB/StringC/g' | sed 's/StringA/StringB/g' | sed 's/StringC/StringA/g'

ปัญหาเกี่ยวกับวิธีการนี้คือสมมติว่า StringC ไม่ได้เกิดขึ้นในไฟล์ ในขณะที่นี่ไม่ใช่ปัญหาในทางปฏิบัติวิธีนี้ยังคงรู้สึกสกปรก - นั่นคือมันรู้สึกเหมือนเป็นโอกาสที่จะเรียนรู้เวทมนตร์ยูนิกซ์มากขึ้น :)

คำตอบ:


11

หากStringBและStringAไม่สามารถปรากฏบนบรรทัดอินพุตเดียวกันคุณสามารถบอกให้ sed ทำการแทนที่ได้หนึ่งวิธีและลองทำวิธีอื่นหากไม่มีการค้นหาสตริงแรก

<file.txt sed -e 's/StringA/StringB/g' -e t -e 's/StringB/StringA/g'

ในกรณีทั่วไปฉันไม่คิดว่าจะมีวิธีง่าย ๆ ในการทำใจให้สบาย โดยวิธีการโปรดทราบว่าข้อกำหนดที่ไม่ชัดเจนถ้าStringAและStringBสามารถทับซ้อนกัน ต่อไปนี้เป็นทางออกของ Perl ซึ่งจะแทนที่สตริงซ้ายสุดและเกิดขึ้นซ้ำ

<file.txt perl -pe 'BEGIN {%r = ("StringA" => "StringB", "StringB" => "StringA")}
                    s/(StringA|StringB)/$r{$1}/ge'

หากคุณต้องการใช้เครื่องมือ POSIX awk เป็นวิธีที่จะไป Awk ไม่มีพื้นฐานสำหรับการแทนที่แบบทั่วไปดังนั้นคุณจำเป็นต้องม้วนตัวเอง

<file.txt awk '{
    while (match($0, /StringA|StringB/)) {
        printf "%s", substr($0, 1, RSTART-1);
        $0 = substr($0, RSTART);
        printf "%s", /^StringA/ ? "StringB" : "StringA";
        $0 = substr($0, 1+RLENGTH)
    }
    print
}'

เมื่อฉันเรียกใช้คำสั่งแรก sed sed: can't read s/StringB/StringA/g: No such file or directoryบอกฉัน ดูเหมือน-e t PATTERNจะไม่เป็นที่เข้าใจกัน
Gyscos

1
@Gyscos มีการหายไป-eก่อนsคำสั่งที่สอง ฉันแก้ไขคำตอบของฉันแล้ว
Gilles 'หยุดความชั่วร้าย'

8

ตอนนี้ฉันกำลังทำบางสิ่งบางอย่างเช่น
...............
ปัญหาของวิธีนี้คือสมมติว่า StringC ไม่ได้เกิดขึ้นในไฟล์

ฉันคิดว่าวิธีการของคุณดีคุณควรใช้อย่างอื่นแทนสตริงสิ่งที่ไม่สามารถเกิดขึ้นในบรรทัด (ในพื้นที่รูปแบบ) ผู้สมัครที่ดีที่สุดคือ\newline
โดยปกติแล้วไม่มีบรรทัดอินพุตในพื้นที่รูปแบบจะมีอักขระนั้นดังนั้นเพื่อสลับการเกิดขึ้นทั้งหมดTHISและTHATในไฟล์คุณสามารถเรียกใช้:

sed 's/THIS/\
/g
s/THAT/THIS/g
s/\n/THAT/g' infile

หรือถ้าคุณสนับสนุน\nใน RHS ด้วย:

sed 's/THIS/\n/g;s/THAT/THIS/g;s/\n/THAT/g' infile

1
นี่คือสิ่งที่สวยงาม ฉันร้องไห้เล็กน้อย อีกวิธีในการขึ้นบรรทัดใหม่ของ RHS คือตัวแปรเชลล์ - ไม่ว่าจะsedรองรับการหลบหนีบางอย่างหรือไม่สำคัญน้อยลงถ้าคุณเตรียมมาโครสองสามตัวล่วงหน้า ชอบset /THIS /THAT "$(printf \\n/)"; sed "s/$2/\\$4g;s/$3$2/g;s/\\n$3/g"- โง่ที่นี่เป็นที่ยอมรับ แต่มันก็สมเหตุสมผลดีกว่าเมื่อถึงเวลาอื่น - โดยเฉพาะอย่างยิ่งสำหรับชั้นเรียนถ่านและสิ่งที่คล้ายกัน
mikeserv

แล้วผู้ชายล่ะ มีแม้กระทั่งคำตอบเกี่ยวกับที่นี่ เมื่อฉันแสดงความคิดเห็น? ฉันเพิ่งเห็นสิ่งที่ปรากฏขึ้นในรายการแก้ไขเมื่อเร็ว ๆ นี้(อาจจะ)และบรรทัดด้านบนด้านบนของคำตอบก็คือออกเล็ก ๆ น้อย ๆ(ถ้าคุณจะดูแลเกี่ยวกับลินุกซ์ที่ไม่ฝังตัวฉันเดา) ฉันชอบข้อเสนอแนะของ Gilles ที่นั่น - ถ้าคุณไม่ทำงานนานsed, ค่าใช้จ่ายในการแยกที่คงที่ด้วยeคือฝันร้ายนะ ในบันทึกอื่น - ฉันเล่นกับpasteทั้งวัน ฉันทำ parser ตัวเลือก - เหมือนcolumnชนิดของ มันแค่ขีดกลางสำหรับสตริงอินพุตและสตริงเข้าด้วยกัน
mikeserv

3

ฉันคิดว่ามันใช้ได้อย่างสมบูรณ์ในการใช้สตริง "nonce" เพื่อสลับสองคำ หากคุณต้องการวิธีแก้ปัญหาทั่วไปที่มากกว่านี้

sed 's/_/__/g; s/you/x_x/g; s/me/you/g; s/x_x/me/g; s/__/_/g' <<<"say you say me"

ที่ให้ผลผลิต

say me say you

โปรดทราบว่าคุณต้องการการทดแทนเพิ่มเติมสองรายการที่นี่เพื่อหลีกเลี่ยงการแทนที่x_xหากคุณมีสตริง "x_x" แต่ถึงกระนั้นมันก็ดูง่ายกว่าawkทางออกสำหรับฉัน


นั่นเป็นสิ่งที่ Asker บอกว่าพวกเขากำลังทำอยู่
roaima

1
ใช่ฉันมองข้ามว่าในตอนแรก (ดูประวัติการแก้ไข) แต่โซลูชันที่ฉันกำหนดแตกต่างกันเนื่องจากทำงานได้แม้ว่าสตริงการแทนที่ (ที่นี่ "x_x") จะเกิดขึ้นในสตริงดั้งเดิมดังนั้นจึงเป็นเรื่องทั่วไปมากกว่า
David Ongaro

สมาร์ท แต่มีการจับ หาก StringA หรือ StringB มี_หนึ่งความต้องการที่จะปรับ_ตัวเอง (เลือกตัวละครอื่น) หรือสตริงลำบาก (ดำเนินการs/_/__/gเกี่ยวกับมันก่อนดูเหมือนว่าดีกว่า) โซลูชันของคุณไม่สามารถนำไปใช้สุ่มสี่สุ่มห้าเพื่อสลับสตริงที่กำหนดเองได้
Kamil Maciorowski

@KamilMaciorowski ฉันไม่เข้าใจว่าคุณหมายถึงอะไร? ฉันสมัครs/_/__/gล่วงหน้าจริง ๆ อาจแค่แสดง testcase ที่ล้มเหลว
David Ongaro

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