web-dev-qa-db-ja.com

ファイルの先頭からバイトを削除する最良の方法?

今日、800 MBの混合テキスト/バイナリファイルから、最初の1131バイトを削除する必要がありました。これは、新しいリポジトリ用にハッキングしている、フィルタリングされたSubversionダンプです。これを行う最良の方法は何ですか?

最初に私が試した

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

しかし、スキップ後、これはファイルの残りを一度に1バイトずつ、つまり非常にゆっくりとコピーします。最後に私は解決しました、これを512の3ブロックに丸めるために405バイトが必要でしたが、スキップすることができました

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

どれがかなり早く完了しましたが、もっと簡単で良い方法があったに違いありませんか?忘れていた別のツールはありますか?ありがとう!

64
Rup

あなたはbsを切り替えてオプションをスキップすることができます:

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

このようにして、操作はより大きなブロックから利益を得ることができます。

そうでない場合は、tailで試すことができます(ただし、バイナリファイルで使用するのは安全ではありません)。

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を出力します。 2つ目は1131バイトを読み取り、それらを破棄します。次に、最後のものはその標準入力からfiltered.dumpの残りのバイトを読み取り、それらをtrimmed.dumpに書き込みます。

67
marco

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は、skipオプションの値をブロックではなくバイトとして解釈するようにddに指示し、それを簡単にします。

次のように、サブシェルと2つの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
15
maxschlepzig

ファイルシステムとLinuxカーネルがそれをサポートしている場合、変更を適用したい場合は fallocate を試すことができます。最良の場合、データはありませんIOまったく:

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

どこ <magic>は、ファイルシステム、Linuxバージョン、およびファイルタイプ( FALLOC_FL_COLLAPSE_RANGEまたはFALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEは内部で使用できます )。

7
jfs

_count=0_を使用する必要があります。これは、可能な限り単純なlseek()です。

このような:

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

ddは、入力ファイル記述子を1131バイトのオフセットにlseek()し、catは、残っているものを出力にコピーするだけです。

3
mikeserv

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
wop

@maxschlepzigはオンラインライナーを要求します。これはPerlの1つです。 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 /秒を提供します。

2
Ole Tange