เพิ่มบรรทัดไปยังจุดเริ่มต้นและจุดสิ้นสุดของไฟล์ขนาดใหญ่


23

ฉันมีสถานการณ์ที่จะเพิ่มบรรทัดในจุดเริ่มต้นและจุดสิ้นสุดของไฟล์ขนาดใหญ่

ฉันลองแล้วตามที่แสดงด้านล่าง

  • สำหรับบรรทัดแรก:

    sed -i '1i\'"$FirstLine" $Filename
  • สำหรับบรรทัดสุดท้าย:

    sed -i '$ a\'"$Lastline" $Filename  

แต่ปัญหาของคำสั่งนี้คือการผนวกบรรทัดแรกของไฟล์และทำการข้ามทั้งไฟล์ สำหรับบรรทัดสุดท้ายมันจะข้ามไฟล์ทั้งหมดอีกครั้งและต่อท้ายบรรทัดสุดท้าย เนื่องจากไฟล์มีขนาดใหญ่มาก (14GB) สิ่งนี้ใช้เวลานานมาก

ฉันจะเพิ่มบรรทัดไปยังจุดเริ่มต้นและอีกบรรทัดในตอนท้ายของไฟล์ขณะอ่านไฟล์เพียงครั้งเดียว

คำตอบ:


20

sed -iใช้ tempfiles เป็นรายละเอียดการใช้งานซึ่งเป็นสิ่งที่คุณกำลังประสบอยู่ อย่างไรก็ตามการเตรียมข้อมูลไปยังจุดเริ่มต้นของสตรีมข้อมูลโดยไม่ต้องเขียนทับเนื้อหาที่มีอยู่นั้นจำเป็นต้องเขียนไฟล์ซ้ำไม่มีวิธีใดที่จะแก้ไขได้แม้ว่าจะหลีกเลี่ยงsed -iก็ตาม

หากการเขียนไฟล์ใหม่ไม่ใช่ตัวเลือกคุณอาจพิจารณาจัดการไฟล์เมื่ออ่านเช่น:

{ echo some prepended text ; cat file ; } | command

นอกจากนี้ sed ยังใช้สำหรับแก้ไขสตรีม - ไฟล์ไม่ใช่สตรีม ใช้โปรแกรมที่มีไว้สำหรับวัตถุประสงค์นี้เช่น ed หรือ ex -iตัวเลือกในการ sed ไม่ได้เป็นเพียงไม่พกพาก็ยังจะทำลาย symlinks ใด ๆ กับไฟล์ของคุณเพราะมันเป็นหลักจะลบมันและสร้างมันซึ่งเป็นจุดหมาย

คุณสามารถทำได้ในคำสั่งเดียวด้วยedเช่น:

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

โปรดทราบว่าขึ้นอยู่กับการนำไปใช้งานของ ed อาจใช้ไฟล์เพจจิ้งโดยกำหนดให้คุณมีพื้นที่ว่างอย่างน้อยที่สุด


สวัสดีคำสั่ง ed ที่ u ให้ไว้นั้นทำงานได้ดีมากสำหรับไฟล์ขนาดใหญ่ แต่ฉันมีไฟล์ขนาดใหญ่ 3 ไฟล์เช่น Test, Test1, Test 2 ฉันได้รับคำสั่งเช่น ed -s Tes * << 'EOF' 0a เพิ่มบรรทัดเหล่านี้ไปยังจุดเริ่มต้น $ a ต่อท้ายบรรทัดเหล่านี้ต่อท้าย W EOF แต่ใช้ไฟล์ทดสอบเท่านั้นและเพิ่มบรรทัดแรก / สุดท้าย เราจะเปลี่ยนแปลงคำสั่งเดียวกันได้อย่างไรเพื่อให้มันต้องเพิ่มบรรทัดแรกและบรรทัดสุดท้ายในไฟล์ทั้งหมด
UNIX ที่ดีที่สุด

@UNIXbest - ใช้forลูป:for file in Tes*; do [command]; done
Chris Down

สวัสดีฉันใช้คำสั่งด้านล่างสำหรับไฟล์ใน Tes *; ทำ ed -s Tes * << 'EOF' 0a HEllO HDR $ a Hello TLR ทำ EOF เสร็จแล้วแต่ยังเขียนเป็นไฟล์แรก
UNIX ที่ดีที่สุด

ขวาเพราะคุณจำเป็นต้องใช้"$file"ไม่เป็นอาร์กิวเมนต์ไปTes* ed
Chris Down

2
@UNIX ที่ดีที่สุดหากปัญหาของคุณได้รับการแก้ไขโดยคำตอบนี้คุณควรพิจารณายอมรับมัน
โจเซฟอาร์

9

โปรดทราบว่าหากคุณต้องการหลีกเลี่ยงการจัดสรรสำเนาทั้งหมดของไฟล์บนดิสก์คุณสามารถทำได้:

sed '
1i\
begin
$a\
end' < file 1<> file

ที่ใช้ความจริงที่ว่าเมื่อ stdin / stdout เป็นไฟล์ให้sed อ่านและเขียนโดย block ดังนั้นที่นี่ก็โอเคที่จะแทนที่ไฟล์ที่อ่านตราบเท่าที่บรรทัดแรกที่คุณเพิ่มมีขนาดเล็กกว่าsedขนาดบล็อก (ควรเป็นขนาด 4k หรือ 8k)

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

นอกจากนี้โปรดทราบว่าหากคุณsedไม่ใช่ GNU sedนั่นจะไม่สามารถใช้งานกับข้อมูลไบนารีได้ (แต่เนื่องจากคุณใช้-iอยู่คุณกำลังใช้ GNU อยู่)


ข้อผิดพลาดนี้สำหรับฉันใน Ubuntu 16.04
Csaba Toth

4

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

  • echo / cat ง่าย ๆ

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
  • awk / gawk เป็นต้น

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 

    awkและ ilk อ่านไฟล์ทีละบรรทัด BEGIN{}บล็อกจะถูกดำเนินการก่อนที่จะสายแรกและEND{}บล็อกหลังจากบรรทัดสุดท้าย ดังนั้นคำสั่งprint "first" at the beginning, then print every line in the file and print "last" at the endดังกล่าวหมายถึง

  • Perl

    perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile

    นี่คือสิ่งเดียวกับเพ่งพิศเหนือเขียนเพียงใน Perl


1
โปรดทราบว่าในทุกกรณีคุณจะต้องใช้พื้นที่เพิ่มเติมอย่างน้อย 14GB สำหรับไฟล์ใหม่
Chris Down

@ChrisDown ดีจุดฉันแก้ไขคำตอบของฉันเพื่อให้ชัดเจน ฉันคิดว่านั่นไม่ใช่ปัญหาตั้งแต่ OP ใช้sed -iซึ่งสร้างไฟล์ชั่วคราว
terdon

3

ฉันชอบแบบที่เรียบง่ายกว่ามาก:

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

สิ่งนี้จะแปลงไฟล์:

asdf
qwer

ไปที่ไฟล์:

foo
asdf
qwer
bar

2

คุณสามารถใช้ Vim ในโหมด Ex:

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 เลือกบรรทัดแรก

  2. i แทรกข้อความและขึ้นบรรทัดใหม่

  3. $ เลือกบรรทัดสุดท้าย

  4. a ต่อท้ายข้อความและขึ้นบรรทัดใหม่

  5. x บันทึกและปิด


ถ้าเราต้องการทำสิ่งนี้กับไฟล์หลาย ๆ ไฟล์
geoyws

1
@geoyws ที่ไม่ได้อยู่ในขอบเขตสำหรับคำถามนี้จริงๆ
Steven Penny

คุณแน่ใจหรือไม่ว่าเป็น $ a และไม่ใช่% a
Carlos Robles

2

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

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

หรือคุณสามารถรวมคำสั่งสองคำสั่งในหนึ่งคำสั่ง

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

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

¹ Linux มีวิธีการแทรกข้อมูลลงในไฟล์ แต่สามารถแทรกบล็อกระบบไฟล์ได้เต็มจำนวนเท่านั้นโดยไม่สามารถแทรกสตริงที่มีความยาวตามอำเภอใจได้ มันมีประโยชน์สำหรับบางแอปพลิเคชันเช่นฐานข้อมูลและเครื่องเสมือน แต่ไม่มีประโยชน์สำหรับไฟล์ข้อความ


ไม่จริง. ดูที่fallocate()มีFALLOC_FL_INSERT_RANGEใน XFS และ ext4 ในเมล็ดทันสมัย ​​(4.xx) man7.org/linux/man-pages/man2/fallocate.2.html
Eric

@Eric คุณสามารถแทรกทั้งบล็อกได้ แต่ไม่ใช่ความยาวไบต์แบบสุ่มอย่างน้อยต้องเป็น Linux 4.15.0 พร้อม ext4 มีระบบไฟล์ที่สามารถแทรกความยาวไบต์ได้หรือไม่?
Gilles 'หยุดความชั่วร้าย'

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

0
$ (echo "Some Text" ; cat file1) > file2

4
ไม่สามารถตอบรับรหัสได้เท่านั้นโปรดปรับปรุงคำตอบของคุณ
Networker

พิจารณาขยายคำตอบของคุณเพื่อรวมคำอธิบายของข้อเสนอแนะของคุณหรือลิงก์ไปยังเอกสารประกอบที่สนับสนุนโซลูชันของคุณ
HalosGhost

-1

เคอร์เนล Linux สมัยใหม่ (สูงกว่า 4.1 หรือ 4.2) สนับสนุนการแทรกข้อมูลที่จุดเริ่มต้นของไฟล์ผ่านการfallocate()เรียกระบบด้วยFALLOC_FL_INSERT_RANGEบนระบบไฟล์ ext4 และ xfs ในสาระสำคัญนี่คือการดำเนินการขยับเชิงตรรกะ: ข้อมูลจะถูกย้ายอย่างมีเหตุผลที่ออฟเซ็ตที่สูงขึ้น

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

ฉันไม่รู้อรรถประโยชน์ใด ๆ ของ linux ที่พร้อมใช้งานที่จัดการไฟล์ส่วนขยาย แต่ก็ไม่ยากที่จะเขียน: รับไฟล์ descriptor และการโทรfallocate()ด้วยอาร์กิวเมนต์ที่เหมาะสม สำหรับรายละเอียดเพิ่มเติมโปรดดูหน้า man ของการfallocateเรียกระบบ: http://man7.org/linux/man-pages/man2/fallocate.2.html


ยูทิลิตี้ไม่ได้เป็นปัญหา (สมมติว่า Linux ที่ไม่ได้ฝังตัว): util-linux มีfallocateยูทิลิตี ปัญหาคือความละเอียดของบล็อกทั้งหมดทำให้สิ่งนี้ไร้ประโยชน์สำหรับไฟล์ข้อความส่วนใหญ่ ปัญหาอีกประการหนึ่งก็คือการจัดสรรช่วงและการปรับเปลี่ยนในภายหลังจะไม่ปรมาณู นี่ไม่ได้แก้ปัญหาตรงนี้
Gilles 'หยุดความชั่วร้าย'

ความละเอียดเป็นข้อแม้ที่ฉันได้กล่าวถึงไปแล้วและไม่มันไม่ได้ทำให้ไร้ประโยชน์ขึ้นอยู่กับแอปพลิเคชัน คุณเห็นคำถามที่ว่าอะตอมมีความสำคัญหรือไม่ ฉันเห็น แต่ปัญหาของการแสดงเท่านั้น ถึงกระนั้น syscall นี้ก็ดูเหมือนจะเป็นอะตอม: elixir.bootlin.com/linux/latest/source/fs/open.c#L228และถ้าอะตอมมีความสำคัญ (มันไม่ใช่ แต่บอกว่ามันเป็นเรื่องของการโต้แย้ง) เพียงแค่ใช้การล็อคไฟล์ (ชี้ให้ฉันไปที่ที่อยู่ในรหัสเคอร์เนลที่fallocateอะตอมมิกขาดโปรดฉันอยากรู้)
Eric
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.