Cara terbaik untuk menghapus byte dari awal file?


62

Hari ini saya harus menghapus 1131 byte pertama dari file teks / biner campuran 800MB, sebuah subversi sampah yang disaring. Saya meretas untuk repositori baru. Apa cara terbaik untuk melakukan ini?

Untuk mulai dengan saya mencoba

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

tetapi setelah melewati ini menyalin sisa file satu byte pada satu waktu, yaitu sangat lambat. Pada akhirnya saya berhasil saya perlu 405 byte untuk membulatkan ini hingga tiga blok 512 yang bisa saya lewati

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

yang selesai cukup cepat tetapi pasti ada cara yang lebih sederhana / lebih baik? Apakah ada alat lain yang saya lupa? Terima kasih!


ddadalah alat yang tepat untuk pekerjaan itu - sepertinya Anda datang dengan solusi yang bagus dan elegan untuk masalah Anda.
Justin Ethier

Jawaban:


64

Anda dapat beralih bs dan melewati opsi:

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

Dengan cara ini operasi dapat mengambil manfaat dari blok yang lebih besar.

Jika tidak, Anda bisa mencoba dengan tail (walaupun tidak aman untuk menggunakannya dengan file biner):

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

Akhirnya, Anda dapat menggunakan instance 3 dd untuk menulis sesuatu seperti ini:

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

di mana dd pertama mencetak output standarnya difilter.dump; yang kedua hanya membaca 1131 byte dan membuangnya; kemudian, yang terakhir membaca dari input standarnya byte yang tersisa dari filtered.dump dan menuliskannya ke trimmed.dump.


6
Terima kasih! Saya tidak tahu bahwa input yang disalurkan ke proses kedua seperti itu - itu sangat rapi. Saya tidak percaya saya tidak berpikir bs=1131 skip=1: - /
Rup

2
Sebagian besar implementasi modern dari utilitas shell bekerja dengan benar dengan file biner (yaitu mereka tidak memiliki masalah dengan karakter nol dan tidak akan memasukkan baris baru tambahan di akhir file). Tentu saja implementasi GNU dan * BSD aman.
Gilles 'SO- stop being evil'

Apa artinya "tidak aman untuk menggunakannya dengan file biner"?
Scott

18

Tidak yakin kapan skip_bytesditambahkan, tetapi untuk melewati 11 byte pertama yang Anda miliki:

# 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

Di mana iflag=skip_bytesmemberitahu dd untuk menginterpretasikan nilai skipopsi sebagai byte alih-alih blok, membuatnya langsung.


Tentunya keuntungan kecepatan untuk file besar dan sejumlah kecil data yang akan dihapus.
sstn

Ini adalah jawaban terbaik, karena ini bekerja untuk setiap ukuran blok misalnyaiflag=skip_bytes skip=1234 bs=1M
phiresky

15

Anda dapat menggunakan sub-shell dan dua ddpanggilan seperti ini:

$ ( 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
Terima kasih - Saya tidak tahu input yang disalurkan melanjutkan ke proses kedua seperti itu, saya kira itu adalah sub shell? Saya pasti akan mengingatnya! Saya telah memberi tanda centang pada Marco karena dia lebih dulu di sini, tetapi +1 dan terima kasih atas jawabannya!
Rup

1
@Rup, ya, sub-shell - dibuat melalui tanda kurung - menyediakan deskriptor file stdin dan kedua panggilan dd berturut-turut mengkonsumsi input darinya. Ya - Marco mengalahkan saya dengan 29 detik :)
maxschlepzig

6

Jika filesystem dan kernel Linux mendukungnya maka Anda bisa mencoba fallocatejika Anda ingin membuat perubahan di tempat: dalam kasus terbaik tidak ada data IO sama sekali:

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

di mana <magic>tergantung pada sistem file, versi Linux, dan jenis file ( FALLOC_FL_COLLAPSE_RANGEatau FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEdapat digunakan secara internal ).


1
Ini adalah metode yang saya sukai, tetapi menjalankan ini dalam sebuah wadah memiliki masalah. stackoverflow.com/questions/31155591/…
michaelcurry

3

Anda harus menggunakan count=0- itu sesederhana lseek()mungkin.

Seperti ini:

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

ddakan lseek()deskriptor file input ke offset 1131 byte, dan kemudian cathanya akan menyalin apa pun yang tersisa ke output.


2

Namun cara lain untuk menghapus byte utama dari file (tanpa menggunakan ddsama sekali) adalah dengan menggunakan xxddan masing sed- tailmasing.

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

Itu rapi, tapi saya rasa saya lebih suka hanya bekerja dengan file dalam biner daripada mengubahnya ke dan dari hex.
Rup

2

@maxschlepzig meminta liner online. Berikut ini satu perl. Dibutuhkan 2 argumen: Dari byte dan panjang. File input harus diberikan oleh '<' dan output akan berada di 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

Jika panjangnya lebih besar dari file, sisa file akan disalin.

Di sistem saya ini memberikan 3,5 GB / s.


Saya pikir tantangan satu barisnya adalah membuat Anda membuktikan bahwa solusi bahasa scripting lebih baik daripada solusi shell satu-barisnya. Dan saya lebih suka miliknya: lebih pendek dan lebih jelas bagi saya. Jika kinerja Anda lebih baik, itu karena Anda menggunakan ukuran blok yang lebih besar daripada dia yang juga mudah dinaikkan dalam versinya.
Rup

@Rup Alas, tapi tidak. Anda sepertinya lupa bahwa ddtidak menjamin pembacaan penuh. Coba: ya | dd bs = 1024k hitung = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange

Juga solusi saya tidak akan membaca byte yang tidak Anda butuhkan (yang bisa beberapa terabyte panjang).
Ole Tange
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.