web-dev-qa-db-ja.com

猫| dd一貫性のない動作

特定のファイルから、特定のサイズになるまでゼロが埋め込まれたコピーを作成する必要があります。

以下でファイルを作成するとします。

echo test >testfile

次のコマンドの出力には一貫性がありません。

cat testfile /dev/zero | dd bs=256k count=1 status=none | od -c

これは私が期待する出力です。

0000000   t   e   s   t  \n  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
1000000

ただし、次のいずれかをランダムに取得することもできます。

0000000   t   e   s   t  \n
0000005
0000000   t   e   s   t  \n  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0400000  \0  \0  \0  \0  \0
0400005

このコマンドの動作に一貫性がないのはなぜですか?

Ddが最初のファイルの最後でパイプを切断している場合でも、128kの結果は奇妙です。 16.04、18.04、19.04のシステムでは、一貫性のない同じ結果が得られます。

4
John JJ

完全なブロックを指定する必要があります。試してください:

cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | od -c

ドキュメンテーション

man dd

フルブロック
入力の完全なブロックを蓄積します(iflagのみ)

fullblockがないと、バイト数に一貫性がないことに注意してください。

$ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c
5
$ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c
5

iflag=fullbock、一貫した全バイト数が表示されます。

$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c
262144
4
John1024

問題の核心は2つあります。問題の一部は、短いまたは部分的なread()です。 POSIX仕様 ごと:

部分的な入力ブロックは、read()が入力ブロックサイズよりも小さい値を返したブロックです。

これは典型的なパイプであり、まさにそれが問題で起こっていることです。 1つの解決策はGNU拡張_iflag=fullblock_を使用することであり、これはUbuntuが使用するバージョンです。From GNU dd manual

たとえば、パイプから読み取る場合のように入力が短い読み取りを返す可能性がある場合、「iflag = fullblock」は、「count =」が入力読み取り操作をカウントする従来のPOSIX指定の動作ではなく、完全な入力ブロックに対応することを保証します。 。

POSIX ddMirOS ddFreeBSD dd -これらにはそのようなオプションはありません(ただし 要求があった POSIX仕様にそれを追加する)。では、Ubuntuから移植してFreeBSDと言ってもよいddを使用してポータブルスクリプトを作成するにはどうすればよいでしょうか。さて、問題の一部は_count=1_フラグです。 ddに、実行するread()呼び出しの数を通知します。 _dd if=/dev/urandom | strace -e read dd of=/dev/null bs=256k count=1_で複数のトレースを実行すると、常にread()が1つだけ存在することがわかります。 (256kが256 * 1024 = 262144であるため、256,000ではなく262144バイトが読み取られても驚かないでください)

解決策は、パラメーターを反転することです。つまり、ブロックサイズを_bs=1_および_count=256k_にします。このようにして、部分的な読み取りがないことを確認し、常に1バイトを読み取りますが、256k回行います。そして、はい、これは非常に遅く、ギガバイト/テラバイトの範囲のデータではかなり時間がかかります。私のテストでは、_iflag=fullblock_は約100倍高速でした(256kバイトで5ミリ秒と700ミリ秒の差)。ただし、これは移植可能であり、GNU dd拡張に依存する必要がないという利点があります。特にGNU dd

2