มีเหตุผลหลายประการที่การอ่านไฟล์ทั้งหมดในพื้นที่รูปแบบอาจผิดพลาดได้ ปัญหาตรรกะในคำถามโดยรอบบรรทัดสุดท้ายเป็นปัญหาทั่วไป มันเกี่ยวข้องกับsedวงจรของสาย - เมื่อไม่มีอีกแล้วและsedพบ EOF มันผ่าน - มันหยุดการประมวลผล ดังนั้นถ้าคุณอยู่ในบรรทัดสุดท้ายและคุณแนะนำsedให้รับมันก็จะหยุดอยู่ตรงนั้นและไม่ทำอะไรอีก
ที่กล่าวว่าหากคุณต้องการอ่านไฟล์ทั้งหมดลงในพื้นที่รูปแบบจริงๆแล้วมันก็น่าจะคุ้มค่าที่จะพิจารณาเครื่องมืออื่นอยู่ดี ความจริงก็คือsedเป็นตัวแก้ไขกระแสที่มีความหมายเหมือนกัน - มันถูกออกแบบมาเพื่อทำงานเป็นสาย - หรือบล็อกข้อมูลแบบลอจิคัล - ในเวลาเดียวกัน
มีเครื่องมือที่คล้ายกันหลายอย่างที่ติดตั้งดีกว่าเพื่อจัดการบล็อกไฟล์แบบเต็ม edและexตัวอย่างเช่นสามารถทำสิ่งที่sedสามารถทำได้และมีไวยากรณ์ที่คล้ายกัน - และอื่น ๆ นอกเหนือจาก - แต่แทนที่จะทำงานเฉพาะในอินพุตสตรีมในขณะที่เปลี่ยนเป็นเอาต์พุตเช่นเดียวกับsedพวกเขายังรักษาไฟล์สำรองชั่วคราวในระบบไฟล์ . งานของพวกเขามีบัฟเฟอร์ไปยังดิสก์ตามความจำเป็นและพวกเขาจะไม่ลาออกอย่างกะทันหันในตอนท้ายของไฟล์(และมีแนวโน้มที่จะระเบิดมากน้อยมักจะภายใต้ความเครียดบัฟเฟอร์) ยิ่งไปกว่านั้นพวกเขายังมีฟังก์ชั่นที่มีประโยชน์มากมายซึ่งsedไม่เรียงลำดับที่ไม่สมเหตุสมผลในบริบทของสตรีมเช่นเครื่องหมายบรรทัดเลิกทำบัฟเฟอร์ที่ตั้งชื่อเข้าร่วมและอื่น ๆ
sedจุดแข็งหลักของมันคือความสามารถในการประมวลผลข้อมูลทันทีที่อ่านได้อย่างรวดเร็วมีประสิทธิภาพและในสตรีม เมื่อคุณ slurp ไฟล์ที่คุณทิ้งและคุณมักจะพบปัญหากรณีขอบเช่นปัญหาบรรทัดสุดท้ายที่คุณพูดถึงและบัฟเฟอร์ overruns และประสิทธิภาพสุดขีด - ในขณะที่ข้อมูลจะแยกวิเคราะห์ยาวขึ้นเวลาประมวลผลของเครื่องมือ regexp เมื่อระบุการจับคู่ เพิ่มขึ้นชี้แจง
เกี่ยวกับจุดสุดท้ายนั้นโดยวิธี: ในขณะที่ฉันเข้าใจs/a/A/gกรณีตัวอย่างน่าจะเป็นเพียงตัวอย่างไร้เดียงสาและอาจไม่ใช่สคริปต์จริงที่คุณต้องการรวบรวมในอินพุตคุณอาจพบว่ามันคุ้มค่าในการทำความคุ้นเคยกับy///. หากคุณมักจะพบว่าตัวเองกำลังgทดแทนตัวละครตัวหนึ่งไปอีกตัวหนึ่งแบบ lobally นั่นyอาจจะมีประโยชน์มากสำหรับคุณ มันคือการเปลี่ยนแปลงเมื่อเทียบกับการทดแทนและเร็วกว่าเพราะไม่ได้หมายความว่า regexp จุดหลังนี้สามารถทำให้มีประโยชน์เมื่อพยายามรักษาและทำซ้ำ//ที่อยู่เปล่า ๆเพราะมันไม่ได้ส่งผลกระทบต่อพวกเขา แต่จะได้รับผลกระทบจากพวกเขา ไม่ว่าในกรณีใดy/a/A/ก็เป็นวิธีที่ง่ายกว่าในการทำสิ่งเดียวกันให้สำเร็จและสามารถสลับได้เช่นกัน:y/aA/Aa/ ซึ่งจะแลกเปลี่ยนบน / ตัวพิมพ์เล็กทั้งหมดบนบรรทัดซึ่งกันและกัน
คุณควรทราบด้วยว่าพฤติกรรมที่คุณอธิบายไม่ใช่สิ่งที่ควรจะเกิดขึ้น
จาก GNU info sedในส่วนข้อบกพร่องที่รายงานโดยทั่วไป :
N คำสั่งในบรรทัดสุดท้าย
sedทางออกส่วนใหญ่โดยไม่พิมพ์อะไรเมื่อNออกคำสั่งในบรรทัดสุดท้ายของไฟล์ GNU sedพิมพ์พื้นที่รูปแบบก่อนออกจากนอกเสียจากว่า-nได้ระบุสวิตช์คำสั่งแล้ว ตัวเลือกนี้เกิดจากการออกแบบ
ตัวอย่างเช่นพฤติกรรมของsed N foo barจะขึ้นอยู่กับว่า foo มีจำนวนบรรทัดคู่หรือคี่ หรือเมื่อเขียนสคริปต์เพื่ออ่านไม่กี่บรรทัดต่อไปดังต่อไปนี้การแข่งขันรูปแบบการใช้งานแบบดั้งเดิมของการsedจะบังคับให้คุณเขียนสิ่งที่ต้องการแทนเพียง/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }/foo/{ N;N;N;N;N;N;N;N;N; }
ไม่ว่าในกรณีใดวิธีแก้ปัญหาที่ง่ายที่สุดคือใช้$d;Nในสคริปต์ที่ต้องอาศัยลักษณะการทำงานดั้งเดิมหรือตั้งค่าPOSIXLY_CORRECTตัวแปรเป็นค่าที่ไม่ว่างเปล่า
POSIXLY_CORRECTตัวแปรสภาพแวดล้อมที่ถูกกล่าวถึงเพราะระบุ POSIX ว่าถ้าsedการเผชิญหน้า EOF เมื่อที่พยายามNมันควรจะลาออกโดยไม่ต้องออก แต่รุ่น GNU จงใจแบ่งมาตรฐานในกรณีนี้ โปรดทราบว่าแม้ในขณะที่พฤติกรรมนั้นเป็นธรรมเหนือข้อสันนิษฐานก็คือกรณีข้อผิดพลาดเป็นหนึ่งในการแก้ไขกระแส - ไม่ slurping ไฟล์ทั้งหมดในหน่วยความจำ
มาตรฐานกำหนดNพฤติกรรมดังนี้:
N
ผนวกอินพุตบรรทัดถัดไปโดยลด\newline ที่ถูกยกเลิกลงในพื้นที่รูปแบบโดยใช้\newline ในตัวเพื่อแยกวัสดุที่ต่อท้ายออกจากวัสดุดั้งเดิม โปรดทราบว่าการเปลี่ยนแปลงหมายเลขบรรทัดปัจจุบัน
หากไม่มีบรรทัดอินพุตถัดไปNคำสั่งกริยาจะแยกไปที่ส่วนท้ายของสคริปต์และออกโดยไม่เริ่มรอบใหม่หรือคัดลอกพื้นที่รูปแบบไปยังเอาต์พุตมาตรฐาน
เมื่อทราบว่ามีบาง GNU-ISMS อื่น ๆ ที่แสดงให้เห็นในคำถาม - โดยเฉพาะอย่างยิ่งการใช้งานของ:ฉลากbไร่และวงเล็บฟังก์ชั่นบริบท{ }เป็นกฎของหัวแม่มือsedคำสั่งใด ๆที่ยอมรับพารามิเตอร์โดยพลการจะเข้าใจการกำหนดขอบเขตที่\newline ในสคริปต์ ดังนั้นคำสั่ง ...
:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...
... ทุกคนมีแนวโน้มที่จะปฏิบัติไม่ถูกต้องขึ้นอยู่กับการsedใช้งานที่อ่านได้ พวกเขาควรจะเขียน:
...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}
เดียวกันถือเป็นจริงสำหรับr, w, t, a, iและ(และอาจจะขึ้นไม่กี่คนที่ฉันลืมในขณะนี้)c ในเกือบทุกกรณีพวกเขาอาจจะเขียน:
sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
"//{ do arbitrary list of commands" -e \}
... โดยที่-eคำสั่ง xecution ใหม่ย่อมาจาก\newline delimiter ดังนั้นที่infoข้อความGNU แนะนำการใช้งานแบบดั้งเดิมsedจะบังคับให้คุณทำ :
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }
... มันควรจะเป็น ...
/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}
... แน่นอนว่าไม่เป็นความจริงเช่นกัน การเขียนบทในวิธีนั้นเป็นเรื่องที่ค่อนข้างงี่เง่า มีวิธีที่ง่ายกว่ามากในการทำเช่นเดียวกันเช่น:
printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
//!g;x;$!d;:nd' -e 'l;$a\' \
-e 'this is the last line'
... ที่พิมพ์:
foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line
... เพราะtคำสั่ง est - เหมือนกับsedคำสั่งส่วนใหญ่- ขึ้นอยู่กับวงจรของวงจรเพื่อรีเฟรชรีจิสเตอร์รีเทิร์นและที่นี่วงจรของไลน์จะได้รับอนุญาตให้ทำงานส่วนใหญ่ได้ นั่นคือการแลกเปลี่ยนอีกอย่างที่คุณทำเมื่อคุณข้ามไฟล์ - รอบของบรรทัดจะไม่รีเฟรชอีกครั้งและการทดสอบจำนวนมากจะทำงานผิดปกติ
คำสั่งข้างต้นไม่เสี่ยงต่อการป้อนข้อมูลที่มากเกินไปเพราะมันจะทำการทดสอบง่ายๆเพื่อตรวจสอบสิ่งที่มันอ่านเมื่อมันอ่าน ด้วยHบรรทัดเก่าทั้งหมดจะต่อท้ายพื้นที่พักสาย แต่ถ้าบรรทัดตรงกับ/foo/นั้นจะแทนที่hพื้นที่ว่างเก่า บัฟเฟอร์มีการxเปลี่ยนแปลงe ถัดไปและs///พยายามใช้การแทนที่แบบมีเงื่อนไขหากเนื้อหาของบัฟเฟอร์ตรงกับ//รูปแบบสุดท้ายที่ระบุ ในคำอื่น ๆ//s/\n/&/3pพยายามที่จะเข้ามาแทนที่การขึ้นบรรทัดใหม่ที่สามในพื้นที่ถือด้วยตัวเองและพิมพ์ผลถ้า/foo/พื้นที่ถือในปัจจุบันตรงกับ หากการทำเช่นนี้tประสบความสำเร็จสคริปต์ก็จะแยกไปที่เลเบลnot delete ซึ่งทำหน้าที่เป็นlook และตัดคำสคริปต์ออก
ในกรณีที่ทั้งคู่/foo/และบรรทัดที่สามไม่สามารถจับคู่ร่วมกันในพื้นที่พักแม้ว่า//!gจะจะเขียนทับบัฟเฟอร์ถ้า/foo/ไม่ตรงหรือถ้ามันถูกจับคู่ก็จะเขียนทับบัฟเฟอร์ถ้า\newline ไม่ตรงกัน(ดังนั้นแทนที่/foo/ด้วย ตัวเอง) การทดสอบที่ละเอียดเล็กน้อยนี้ช่วยป้องกันบัฟเฟอร์ไม่ให้เติมเต็มโดยไม่จำเป็นสำหรับการเหยียดยาวเป็นเวลานาน/foo/และช่วยให้มั่นใจได้ว่ากระบวนการไม่ติดขัดเนื่องจากอินพุตไม่ซ้อนกัน ต่อไปนี้ในกรณีที่ไม่มี/foo/หรือ//s/\n/&/3pล้มเหลวบัฟเฟอร์จะถูกสลับอีกครั้งและทุกบรรทัด แต่สุดท้ายจะถูกลบ
บรรทัดสุดท้าย$!d- บรรทัดสุดท้าย- เป็นการสาธิตอย่างง่ายว่าsedสคริปต์จากบนลงล่างสามารถจัดการหลายกรณีได้อย่างง่ายดาย เมื่อวิธีการทั่วไปของคุณคือการตัดกรณีที่ไม่ต้องการที่เริ่มต้นด้วยวิธีที่ทั่วไปที่สุดและทำงานไปยังกรณีที่เฉพาะเจาะจงที่สุดแล้วขอบสามารถจัดการได้ง่ายขึ้นเพราะพวกเขาได้รับอนุญาตให้ผ่านไปยังจุดสิ้นสุดของสคริปต์ด้วยข้อมูลอื่น ๆ ที่คุณต้องการ ทุกอย่างจะห่อคุณด้วยข้อมูลที่คุณต้องการเท่านั้น แม้ว่าการดึงเคสขอบดังกล่าวออกจากลูปปิดอาจทำได้ยากกว่า
และนี่คือสิ่งสุดท้ายที่ฉันต้องพูดว่า: หากคุณต้องดึงไฟล์ทั้งหมดออกมาจริงๆคุณสามารถยืนทำงานให้น้อยลงได้โดยอาศัยวงจรเส้นเพื่อทำเพื่อคุณ โดยทั่วไปแล้วคุณจะใช้Next และnext สำหรับlookahead - เพราะพวกมันก้าวหน้าไปก่อนวัฏจักรของเส้น แทนที่จะใช้ลูปปิดแบบวนซ้ำซ้อนในวง - เนื่องจากsedวงรอบเป็นเพียงลูปการอ่านอย่างง่ายต่อไป - ถ้าจุดประสงค์ของคุณเพียงเพื่อรวบรวมอินพุตอย่างไม่เจาะจงก็อาจทำได้ง่ายกว่า:
sed 'H;1h;$!d;x;...'
... ซึ่งจะรวบรวมไฟล์ทั้งหมดหรือไปลอง
ข้อความด้านข้างเกี่ยวกับNและพฤติกรรมบรรทัดสุดท้าย ...
ในขณะที่ฉันไม่มีเครื่องมือให้ฉันทดสอบให้พิจารณาว่าNเมื่อการอ่านและการแก้ไขในสถานที่ทำงานแตกต่างกันหากไฟล์ที่แก้ไขเป็นไฟล์สคริปต์สำหรับการอ่านครั้งต่อไป