Bir dosyanın başındaki baytları kaldırmanın en iyi yolu


61

Bugün ilk 1131 byte'ı 800 MB'lık bir karma metin / ikili dosyadan çıkarmak zorunda kaldım, filtrelenmiş bir subversion dökümü yeni bir depo için hackledim. Bunu yapmanın en iyi yolu nedir?

Başlamak için denedim

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

ancak bu atlanmadan sonra, dosyanın geri kalanını her seferinde bir bayt, yani çok yavaş bir şekilde kopyalar. Sonunda çalıştım, atlayabildiğim 512 üç bloka kadar yuvarlamak için 405 bayta ihtiyacım vardı.

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

Bu oldukça hızlı bir şekilde tamamlandı ancak daha basit / daha iyi bir yol olmalıydı? Unuttuğum başka bir araç var mı? Teşekkürler!


ddiş için doğru araçtır - sizin probleminize güzel ve zarif bir çözüm bulmuş gibisiniz.
Justin Ethier,

Yanıtlar:


62

Bs'yi değiştirebilir ve seçenekleri atlayabilirsiniz:

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

Bu şekilde işlem daha büyük bir bloktan yararlanabilir.

Aksi takdirde, kuyruğu deneyebilirsin (ikili dosyalarda kullanmak güvenli olmasa da):

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

Son olarak, bunun gibi bir şey yazmak için 3dd örneklerini kullanabilirsiniz:

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

ilk dd'nin standart çıktıyı yazdırdığı yer: filter.dump; ikincisi sadece 1131 byte okur ve onları atar; daha sonra, sonuncusu standart girdiden süzülen.dump'ın kalan baytını okur ve onları trimmed.dump'a yazar.


6
Teşekkürler! Boru girişinin bu şekilde ikinci bir işleme geçtiğini bilmiyordum - bu çok zarif. bs=1131 skip=1Yine de düşünmedim inanamıyorum : - /
Rup

2
Kabuk hizmet programlarının çoğu modern uygulamaları, ikili dosyalarla doğru çalışır (yani boş karakterlerle sorun yaşamaz ve dosyanın sonuna fazladan bir yeni satır eklemez). Kesinlikle GNU ve * BSD uygulamaları güvenlidir.
Gilles 'SO- kötülük' dur

17

Ne zaman skip_byteseklendiğinden emin değilsiniz, ancak sahip olduğunuz ilk 11 baytı atlamak için:

# 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

Dd'ye iflag=skip_bytes, skipseçeneğin değerini bloklar yerine bayt olarak yorumlamasını söyler ve basitleştirir.


Kesinlikle büyük dosyalar ve kaldırılacak az miktarda veri için hız avantajı.
sstn

Bu en iyi cevap, çünkü her blok büyüklüğü için işe yarıyor, örneğiniflag=skip_bytes skip=1234 bs=1M
phiresky

15

Bir alt kabuk ve ddbunun gibi iki çağrı kullanabilirsiniz:

$ ( 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
Teşekkürler - Borulu girdilerin ikinci bir işleme devam ettiğini bilmiyordum, sanırım bu alt kabuk mu? Bunu kesinlikle hatırlayacağım! Marco'ya keneyi verdim çünkü buraya ilk önce +1 geldi ve cevabınız için teşekkürler!
Rup,

1
@Rup, evet, alt kabuk - parantez içinde yaratılmıştır - bir stdin dosyası tanımlayıcısı sağlar ve her iki dd çağrısı da art arda gelen girdiyi kullanır. Evet - Marco 29 saniye beni dövdü :)
maxschlepzig

6

Dosya sistemi ve Linux çekirdeği bunu destekliyorsa fallocate, değişiklikleri yerinde yapmak istiyorsanız deneyebilirsiniz : en iyi durumda hiç veri girişi yok:

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

nerede <magic>dosya sistemi, Linux sürümü ve dosya türüne bağlıdır ( FALLOC_FL_COLLAPSE_RANGEveya FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEdahili olarak kullanılabilir ).


1
Bu benim tercih ettiğim yöntem, ancak bunu bir kapta çalıştırmak onun sorunları. stackoverflow.com/questions/31155591/…
michaelcurry

3

Kullanmanız gerekir count=0- bu lseek()mümkün olduğunda basit .

Bunun gibi, böyle:

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

ddolacak lseek()bir 1131 byte giriş dosya tanıtıcı ofset ve sonra catbasitçe çıkışa kalır ne olursa olsun kopyalar.


2

Yine (kullanmadan bir dosyadan gelen bayt kaldırmak için başka bir yol ddtüm) kullanmaktır xxdve sedveya tailsırasıyla.

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

Bu çok temiz, ama bence sadece dosya ile birlikte çalışmayı tercih ederim.
Rup

2

@ maxschlepzig çevrimiçi bir liner ister. İşte Perl'de bir tane. 2 argüman alır: Bayt ve uzunluk. Giriş dosyası '<' tarafından verilmelidir ve çıktı stdout'ta olacaktır:

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

Uzunluk dosyadan büyükse, dosyanın geri kalanı kopyalanır.

Sistemimde bu 3.5 GB / sn sağlıyor.


Sanırım tek satırlık mücadelesi, kodlama dili çözümünün tek satırlık kabuk çözümünden daha iyi olduğunu kanıtlamanızı sağlamaktı. Ve onun tercihini yapıyorum: benim için daha kısa ve daha net. Sizinki daha iyi performans gösteriyorsa, onun versiyonunda kolayca yükseltilebilecek ondan daha büyük bir blok boyutu kullanıyorsunuzdur.
Rup

@Rup Alas, ama hayır. ddTam bir okumayı garanti etmediğini unutmuş gibisin. Deneyin: evet | dd bs = 1024k sayısı = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange

Ayrıca benim çözümüm gerekmeyen baytları (birkaç terabayt uzunluğunda olabilir) okumaz.
Ole Tange
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.