วิธีที่ดีที่สุดในการลบไบต์จากจุดเริ่มต้นของไฟล์?


61

วันนี้ฉันต้องลบ 1131 ไบต์แรกออกจากไฟล์ข้อความ / ไบนารีผสม 800MB ซึ่งเป็นการถ่ายโอนข้อมูลการโค่นล้มที่กรองแล้วที่ฉันกำลังแฮกสำหรับพื้นที่เก็บข้อมูลใหม่ วิธีที่ดีที่สุดในการทำเช่นนี้คืออะไร?

เริ่มต้นด้วยฉันพยายาม

dd bs=1 skip=1131 if=filtered.dump of=trimmed.dump

แต่หลังจากข้ามการคัดลอกนี้ส่วนที่เหลือของไฟล์ไบต์ในเวลาคือช้ามาก ในตอนท้ายฉันออกกำลังกายฉันต้องการ 405 ไบต์เพื่อปัดเศษนี้ขึ้นไปสามบล็อกจาก 512 ซึ่งฉันสามารถข้าม

dd if=/dev/zero of=405zeros bs=1 count=405
cat 405zeros filtered.dump | dd bs=512 skip=3 of=trimmed.dump

สิ่งใดเสร็จสมบูรณ์อย่างรวดเร็ว แต่ต้องมีวิธีที่ง่ายกว่า / ดีกว่า มีเครื่องมืออื่นที่ฉันลืมไปหรือไม่? ขอบคุณ!


ddเป็นเครื่องมือที่เหมาะสมสำหรับงาน - ดูเหมือนว่าคุณจะมีทางออกที่ดีและสง่างามสำหรับปัญหาของคุณ
Justin Ethier

คำตอบ:


62

คุณสามารถสลับตัวเลือก bs และข้าม:

dd bs=1131 skip=1 if=filtered.dump of=trimmed.dump

วิธีนี้การดำเนินการจะได้ประโยชน์จากบล็อกที่มากขึ้น

มิฉะนั้นคุณสามารถลองด้วยหาง (แม้ว่ามันจะไม่ปลอดภัยที่จะใช้กับไฟล์ไบนารี):

tail -c +1132 filtered.dump >trimmed.dump

สุดท้ายคุณอาจใช้อินสแตนซ์ 3 dd เพื่อเขียนสิ่งนี้:

dd if=filtered.dump bs=512k | { dd bs=1131 count=1 of=/dev/null; dd bs=512k of=trimmed.dump; }

โดยที่ dd แรกพิมพ์เอาต์พุตมาตรฐาน filtered.dump; อันที่สองเพิ่งอ่าน 1131 ไบต์และโยนมันทิ้งไป จากนั้นส่วนสุดท้ายจะอ่านจากอินพุตมาตรฐานเป็นไบต์ที่เหลืออยู่ของ filtered.dump และเขียนลงใน trimmed.dump


6
ขอบคุณ! ฉันไม่ทราบว่าการป้อนข้อมูล piped ผ่านไปยังกระบวนการที่สองเช่นนั้น - เรียบร้อยมาก ฉันไม่อยากจะเชื่อเลยว่าฉันจะไม่นึกถึงbs=1131 skip=1: - /
รูปี

2
การใช้งานยูทิลิตี้เชลล์ส่วนใหญ่ทำงานได้อย่างถูกต้องกับไฟล์ไบนารี่ (เช่นไม่มีปัญหากับตัวอักษรโมฆะและจะไม่แทรกบรรทัดใหม่ที่ส่วนท้ายของไฟล์) แน่นอนว่าการใช้งาน GNU และ * BSD นั้นปลอดภัย
Gilles

17

ไม่แน่ใจว่าskip_bytesถูกเพิ่มเมื่อใดแต่เมื่อต้องการข้าม 11 ไบต์แรกคุณจะต้อง:

# echo {123456789}-abcdefgh- | 
                              dd bs=4096 skip=11 iflag=skip_bytes
-abcdefgh-
0+1 records in
0+1 records out
11 bytes (11 B) copied, 6.963e-05 s, 158 kB/s

โดยiflag=skip_bytesบอกให้ dd ตีความค่าของskipตัวเลือกเป็นไบต์แทนที่จะเป็นบล็อกทำให้ตรงไปตรงมา


แน่นอนความได้เปรียบด้านความเร็วสำหรับไฟล์ขนาดใหญ่และข้อมูลจำนวนเล็กน้อยที่จะลบออก
SSTN

นี่เป็นคำตอบที่ดีที่สุดเพราะใช้ได้กับทุกขนาดบล็อกเช่นiflag=skip_bytes skip=1234 bs=1M
phiresky

15

คุณสามารถใช้เชลล์ย่อยและการddโทรสองสายดังนี้:

$ ( dd bs=1131 count=1 of=dev_null && dd bs=4K of=out.mp3 ) < 100827_MR029_LobbyControl.mp3
1+0 records in
1+0 records out
1131 bytes (1.1 kB) copied, 7.9691e-05 s, 14.2 MB/s
22433+1 records in
22433+1 records out
91886130 bytes (92 MB) copied, 0.329823 s, 279 MB/s
$ ls -l *
-rw------- 1 max users 91887261 2011-02-03 22:59 100827_MR029_LobbyControl.mp3
-rw-r--r-- 1 max users     1131 2011-02-03 23:04 dev_null
-rw-r--r-- 1 max users 91886130 2011-02-03 23:04 out.mp3
$ cat dev_null out.mp3 > orig
$ cmp 100827_MR029_LobbyControl.mp3 orig

1
ขอบคุณ - ฉันไม่ทราบว่าการป้อนข้อมูล piped ดำเนินต่อไปยังกระบวนการที่สองเช่นนั้นฉันเดาว่าเป็นเชลล์ย่อยหรือไม่ ฉันจำได้แน่นอน! ฉันได้รับเครื่องหมายมาร์โกเพราะเขามาที่นี่ก่อน แต่ +1 และขอบคุณสำหรับคำตอบ!
Rup

1
@Rup, yes, sub-shell - สร้างผ่านวงเล็บ - ให้ไฟล์ stdin descriptor และการเรียก dd ทั้งสองอย่างต่อเนื่องใช้การป้อนข้อมูลจากมัน ใช่ - Marco ชนะฉันไป 29 วินาที :)
maxschlepzig

6

หากระบบไฟล์และเคอร์เนล Linux สนับสนุนคุณสามารถลองfallocateใช้การเปลี่ยนแปลงแบบเดิม: ในกรณีที่ดีที่สุดไม่มีข้อมูล IO เลย:

$ fallocate <magic> -o 0 -l 1131 inplace.dump

ที่<magic>ขึ้นอยู่กับระบบแฟ้มรุ่นลินุกซ์และประเภทของไฟล์ ( FALLOC_FL_COLLAPSE_RANGEหรือFALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEอาจจะใช้ภายใน )


1
นี่เป็นวิธีที่ฉันชอบ แต่การเรียกใช้สิ่งนี้ในคอนเทนเนอร์มีปัญหา stackoverflow.com/questions/31155591/…
michaelcurry

3

คุณควรใช้count=0- เป็นวิธีที่ง่ายlseek()ทุกครั้งที่ทำได้

แบบนี้:

{  dd bs=1131 skip=1 count=0; cat; } <filtered.dump >trimmed.dump

ddจะlseek()อธิบายแฟ้มใส่ไปยัง 1131 ไบต์ชดเชยและจากนั้นcatก็จะคัดลอกสิ่งที่ยังคงอยู่กับการส่งออก


2

อีกวิธีหนึ่งในการลบไบต์นำออกจากไฟล์ (โดยไม่ใช้ddเลย) คือการใช้xxdและsedหรือtailตามลำดับ

bytes=$((1131*2))

xxd -p -c 256 filtered.dump | tr -d '\n' | sed "s/^.\{0,${bytes}\}//" | xxd -r -p > trimmed.dump

bytes=$((bytes + 1)) 
xxd -p -c 256 filtered.dump | tr -d '\n' | tail -c +${bytes} | xxd -r -p > trimmed.dump

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

2

@maxschlepzig ขอสายการบินออนไลน์ นี่คือหนึ่งใน perl มันต้องใช้เวลา 2 อาร์กิวเมนต์: จากไบต์และความยาว ไฟล์อินพุตต้องถูกกำหนดโดย '<' และเอาต์พุตจะเป็น stdout:

perl -e 'sysseek(STDIN,shift,0) || die; $left = shift;
     while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){
        $left -= $read; syswrite(STDOUT,$buf);
     }' 12345678901 19876543212 < bigfile > outfile

หากความยาวใหญ่กว่าไฟล์ไฟล์ที่เหลือจะถูกคัดลอก

ในระบบของฉันนี้ให้ 3.5 GB / s


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

@Rup Alas แต่ไม่ใช่ คุณดูเหมือนจะลืมไปแล้วว่าddไม่รับประกันการอ่านแบบเต็ม ลอง: ใช่ | dd bs = 1024k count = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange

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