แนวคิดของ 'Hold space' และ 'Pattern space' ใน sed


87

ฉันสับสนกับสองแนวคิดใน sed: hold space และ pattern space ใครสามารถช่วยอธิบายพวกเขา?

นี่คือตัวอย่างของคู่มือ:

h H    Copy/append pattern space to hold space.
g G    Copy/append hold space to pattern space.

n N    Read/append the next line of input into the pattern space.

คำสั่งทั้งหกนี้ทำให้ฉันสับสนจริงๆ


4
ลองด้วยตัวคุณเอง:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
choroba

4
อย่าสับสนอย่าใช้พวกเขา สำหรับสิ่งอื่นนอกเหนือจากการแทนที่อย่างง่ายในบรรทัดเดียวคุณควรใช้ awk ไม่ใช่ sed เว้นวรรคช่องว่างรูปแบบและ 95% ของโครงสร้างภาษา sed ถูกคิดค้นขึ้นก่อน awk เมื่อไม่มีทางเลือกอื่นที่ดีกว่า พวกเขาล้าสมัยทันทีที่ awk ถูกประดิษฐ์ขึ้นในกลางทศวรรษ 1970 และปัจจุบันมีเพียงคนที่สนุกกับการแก้ปัญหาโดยใช้ไวยากรณ์ของ seds arcane แทนที่จะทำอย่างเรียบง่ายและเข้าใจใน awk หากคุณใช้มากกว่า s, g และ p (ด้วย -n) ใน sed แสดงว่าคุณกำลังใช้เครื่องมือที่ไม่ถูกต้อง
Ed Morton

27
Morton awk ทำงานกับข้อมูลที่มีโครงสร้าง (แต่ละบรรทัดมีโครงสร้างเหมือนกัน) Sed หมายถึงการทำงานกับข้อมูลสุ่มดิบ คุณจึงใช้ awk แทน sed ไม่ได้
Pithikos

5
info sedผมขอแนะนำให้อ่าน มันละเอียดกว่าหน้าคนเปล่ามาก
Fernando Basso

4
ฉันเห็นด้วยกับพิธิกอส ฉันเดินไปตามเลนเหมือนที่มอร์ตันทำและถามตัวเองด้วยคำถามเดียวกันกับมอร์ตัน อย่างไรก็ตามฉันยังไม่สามารถยกเลิก sed ได้อย่างง่ายดาย
eigenfield

คำตอบ:


116

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

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

นี่คือตัวอย่าง:

sed -n '1!G;h;$p'

(ตัวเลือก -n ระงับการพิมพ์เส้นอัตโนมัติ)

มีสามคำสั่งที่นี่: 1!G, และh มีที่อยู่(บรรทัดแรก) แต่หมายความว่าคำสั่งจะถูกเรียกใช้ทุกที่ยกเว้นในบรรทัดแรก ในทางกลับกันจะดำเนินการในบรรทัดสุดท้ายเท่านั้น สิ่งที่เกิดขึ้นคือ:$p1!G1!$p

  1. บรรทัดแรกจะอ่านและแทรกลงในช่องว่างรูปแบบโดยอัตโนมัติ
  2. ในบรรทัดแรกคำสั่งแรกจะไม่ดำเนินการ hคัดลอกบรรทัดแรกลงในการระงับพื้นที่
  3. ตอนนี้บรรทัดที่สองจะแทนที่สิ่งที่อยู่ในพื้นที่รูปแบบ
  4. ในบรรทัดที่สองก่อนอื่นเราดำเนินการ Gต่อท้ายเนื้อหาของบัฟเฟอร์การระงับเข้ากับบัฟเฟอร์รูปแบบโดยแยกด้วยการขึ้นบรรทัดใหม่ ขณะนี้ช่องว่างรูปแบบประกอบด้วยบรรทัดที่สองขึ้นบรรทัดใหม่และบรรทัดแรก
  5. จากนั้นhคำสั่งจะแทรกเนื้อหาที่ต่อกันของบัฟเฟอร์รูปแบบลงในช่องว่างซึ่งตอนนี้ถือบรรทัดที่กลับด้านสองและหนึ่ง
  6. ไปที่บรรทัดที่สาม - ไปที่จุด (3) ด้านบน

ในที่สุดหลังจากที่บรรทัดสุดท้ายได้รับการอ่านและพื้นที่การระงับ (ที่มีทุกสายก่อนหน้านี้ในการสั่งซื้อกลับ) ได้รับการผนวกเข้ากับพื้นที่รูปแบบ, pพื้นที่รูปแบบจะถูกพิมพ์ด้วย อย่างที่คุณเดาไว้ข้างต้นทำตามtacคำสั่งอย่างแน่นอน- พิมพ์ไฟล์กลับด้าน


3
ตัวเลือก G และ h ทำงานเหมือน "ตัดและต่อท้าย" หรือไม่ ?? ดูเหมือนการดำเนินการ "คัดลอกและต่อท้าย" ไม่ได้
มายล์

อะไรต่อท้ายด้วยรูปแบบและเว้นช่องว่างเมื่อใช้คำสั่งซ้อน (วงเล็บปีกกา) '195,210{/add/p}'…เป็นไปได้ไหมที่จะแยกบรรทัดสุดท้ายของกลุ่มบรรทัดที่เกี่ยวข้องกับรูปแบบ
Sandburg

17

@ เอ็ดมอร์ตัน: ฉันไม่เห็นด้วยกับคุณที่นี่ ฉันพบว่าsedมีประโยชน์และเรียบง่ายมาก (เมื่อคุณรวบรวมแนวคิดของรูปแบบและเก็บบัฟเฟอร์ไว้) เพื่อหาวิธีที่สวยงามในการทำ grepping หลายเส้น

ตัวอย่างเช่นลองใช้ไฟล์ข้อความที่มีชื่อโฮสต์และข้อมูลบางอย่างเกี่ยวกับแต่ละโฮสต์โดยมีขยะจำนวนมากอยู่ระหว่างที่ฉันไม่สนใจ

Host: foo1
some junk, doesnt matter
some junk, doesnt matter
Info: about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Info: a second line about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Host: foo2
some junk, doesnt matter
Info: about foo2 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter

สำหรับฉันสคริปต์ awk เพื่อรับบรรทัดที่มีชื่อโฮสต์และinfoบรรทัดที่เกี่ยวข้องจะใช้เวลามากกว่าที่ฉันสามารถทำได้ด้วย sed:

sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt

ผลลัพธ์ดูเหมือนว่า:

Host: foo1
Info: about foo1 that I really care about!!
Host: foo1
Info: a second line about foo1 that I really care about!!
Host: foo2
Info: about foo2 that I really care about!!

(โปรดทราบว่าHost: foo1จะปรากฏขึ้นสองครั้งในเอาต์พุต)

คำอธิบาย:

  1. -n ปิดใช้งานเอาต์พุตเว้นแต่จะพิมพ์อย่างชัดเจน
  2. การแข่งขันครั้งแรกค้นหาและวางHost:บรรทัดไว้ในบัฟเฟอร์ระงับ (h)
  3. การจับคู่ครั้งที่สองค้นหาข้อมูลถัดไป: บรรทัด แต่บรรทัดปัจจุบันการแลกเปลี่ยน (x) แรกในบัฟเฟอร์รูปแบบที่มีบัฟเฟอร์ค้างและพิมพ์ (p) Host:บรรทัดจากนั้นแลกเปลี่ยนใหม่ (x) และพิมพ์ (p) บรรทัดข้อมูล:

ใช่นี่เป็นตัวอย่างที่เรียบง่าย แต่ฉันสงสัยว่านี่เป็นปัญหาทั่วไปที่ได้รับการจัดการอย่างรวดเร็วโดย sed one-liner สำหรับงานที่ซับซ้อนมากขึ้นเช่นงานที่คุณไม่สามารถพึ่งพาลำดับที่กำหนดและคาดเดาได้ awk อาจเหมาะสมกว่า


2
ในกรณีนี้คุณสามารถใช้ grep ได้:grep 'Host\|Info'
Pithikos

หากมีข้อมูลสองบรรทัดหลังโฮสต์ที่กำหนด @JensJenson ต้องการให้บรรทัดข้อมูลทั้งสองนำหน้าด้วยบรรทัดข้อมูล ฉันคิดว่าฉันจะแก้ไขคำตอบตามนั้น Pithikos, grep จะไม่พอเพียงแล้ว
Aaron McDaid

4
@JensJenson awkรหัส sed ของคุณก็สั้นเหมือนกัน:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Aaron McDaid

13

แม้ว่าคำตอบและตัวอย่างของ @ มกราคมจะดี แต่คำอธิบายก็ไม่เพียงพอสำหรับฉัน ฉันต้องค้นหาและเรียนรู้มากมายจนกว่าฉันจะเข้าใจว่ามันsed -n '1!G;h;$p'ทำงานอย่างไร ดังนั้นฉันต้องการอธิบายรายละเอียดเกี่ยวกับคำสั่งสำหรับคนอย่างฉัน

ก่อนอื่นมาดูกันว่าคำสั่งทำอะไร

$ echo {a..d} | tr ' ' '\n' # Prints from 'a' to 'd' in each line
a
b
c
d
$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;$p'
d
c
b
a

มันย้อนกลับอินพุตเหมือนtacคำสั่งทำ

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

Read line    Pattern Space / Hold Space    Command executed
-----------------------------------------------------------
a            a$                            h
b            b\na$                         1!G;h
c            c\nb\na$                      1!G;h
d            d\nc\nb\na$                   1!G;h;$p

ในบรรทัดสุดท้ายให้$pพิมพ์d\nc\nb\na$ที่ฟอร์แมตเป็น

d
c
b
a

หากคุณต้องการดูพื้นที่รูปแบบสำหรับแต่ละบรรทัดคุณสามารถเพิ่มlคำสั่งได้

$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;l;$p'
a$
b\na$
c\nb\na$
d\nc\nb\na$
d
c
b
a

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

นอกจากนี้เอกสาร GNU sedและบทช่วยสอน Sed ของ Bruce Barnett ก็เป็นข้อมูลอ้างอิงที่ดีมาก


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