「Dd」の版間の差分
157行目: | 157行目: | ||
=== バイナリファイルにブロック単位のパッチをインプレースに適用する === |
=== バイナリファイルにブロック単位のパッチをインプレースに適用する === |
||
− | It is not an uncommon practice to use ''dd'' as a feature-limited binary file patcher in automated shell scripts since it is capable to {{ic|seek}} the output file by given offset before writing, and to block-by-block (or byte-y-byte if {{ic|1=bs=1}}) in-place patching the output file by adding the {{ic|1=conv=notrunc}} option. |
||
+ | 自動シェルスクリプトで機能が限定されたバイナリファイルパッチャーとして ''dd'' を使用することは珍しいことではありません。これは、書き込み前に指定されたオフセットによって出力ファイルを {{ic|seek}} することができ、ブロックすることができるためです。 {{ic|1=conv=notrunc}} オプションを追加して、出力ファイルにブロック単位 (または {{ic|1=bs=1}} の場合はバイト y バイト) でインプレースパッチを適用します。 |
||
− | For example, to modify the timestamp part of first member in a {{man|5|cpio|Portable ASCII Format}} archive, which starts at the 49th byte of the file (or with offset of {{ic|0x30}} if you prefer hex notation): |
||
+ | |||
+ | たとえば、{{man|5|cpio|Portable ASCII Format}} アーカイブ内の最初のメンバーのタイムスタンプ部分を変更するには、ファイルの 49 番目のバイトから始まります (または、次の場合は {{ic|0x30}} のオフセットで始まります)。16 進表記を好む場合): |
||
$ touch a-randomly-chosen-file |
$ touch a-randomly-chosen-file |
||
166行目: | 167行目: | ||
$ printf '%011o' "$(date -d "2019-12-21 00:00:00" +%s)" | dd conv=notrunc of=example-modify-ts.cpio seek='''48''' oflag=seek_bytes |
$ printf '%011o' "$(date -d "2019-12-21 00:00:00" +%s)" | dd conv=notrunc of=example-modify-ts.cpio seek='''48''' oflag=seek_bytes |
||
− | {{note| |
+ | {{note|現在文書化されていない [https://github.com/coreutils/coreutils/commit/140eca15c4a3d3213629a048cc307fde0d094738 {{ic|seek_bytes}} 出力フラグ] は、出力への {{man|2|write}} を開始する前にブロックではなくバイトのオフセットで出力をシークするために上記に追加されています。}} |
− | {{tip| |
+ | {{tip|コマンドライン入力の 16 進表記からバイト ストリームを出力するには、{{man|1|basenc|base16}} および/または {{man|1|printf}} を使用します。}} |
+ | {{tip|この機能では、''dd'' の代わりに、シェルが開いたファイルディスクリプタに対して {{man|2|lseek}} を呼び出すことをサポートするシェルを使用することを検討することができます。: |
||
− | {{tip|In this feature grid (''i.e.'' {{man|2|write}} ''with offset, with'' no truncation), instead of using ''dd'', one may consider using a shell that supporting {{man|2|lseek}} operation on shell-opened file descriptor if in case of: |
||
+ | * ''dd'' の入力ファイルは {{man|2|splice}} システムコールを利用するプログラムと接続されたパイプであり、ユーザはより良いパフォーマンスのために ''dd'' の不必要なユーザ空間 I/O を避けたいと考えています。 |
||
− | * the input file of ''dd'' is a pipe connected with a program that utilize {{man|2|splice}} system call, and the user want to avoid unnecessary userspace I/O of ''dd'' for a better performance |
||
+ | * または、シェルスクリプトのループ内で頻繁に {{man|2|fork}} が発生するのを避け、パフォーマンスペナルティを下げたい。 |
||
− | * or, to avoid frequently {{man|2|fork}} in shell script loop to reduce performance penality |
||
+ | その場合、まずそのシェルにファイルディスクリプタを開かせ、そのファイルディスクリプタに対してシーク操作を行い、{{man|2|splice}} システムコールを使う対応するユーティリティの出力端としてこのファイルディスクリプタを割り当てる必要があります(あるいは{{man|1|zshmodules|sysseek}} のようにフォークしないシェル内蔵コマンドもあります):{{hc|$ zsh|<nowiki>$ local +xr openToWriteFD |
||
− | Then it would be necessary to let that shell open the file descriptor at first, and perform some seeking operation on the file descriptor to assign this file descriptor as output end of corresponding utility that use {{man|2|splice}} system call (or a shell builtin command does not forking, as in following example of {{man|1|zshmodules|sysseek}}):{{hc|$ zsh|<nowiki>$ local +xr openToWriteFD |
||
$ zmodload zsh/system |
$ zmodload zsh/system |
||
$ sysopen -wo cloexec -u openToWriteFD example-modify-ts.cpio |
$ sysopen -wo cloexec -u openToWriteFD example-modify-ts.cpio |
||
$ sysseek -u $openToWriteFD 48 |
$ sysseek -u $openToWriteFD 48 |
||
$ printf '%011o' "$(date -d "2019-12-21 00:00:00" +%s)" >&${openToWriteFD}</nowiki>}} |
$ printf '%011o' "$(date -d "2019-12-21 00:00:00" +%s)" >&${openToWriteFD}</nowiki>}} |
||
+ | {{warning|オフセットを使用して書き込む必要があるプログラムが実際に {{man|2|splice}} を使用していることを確認できない場合は、このアプローチの使用を避けてください (これは、プログラムが出力に対していかなる種類のシークや切り捨ても実行しないことを意味します)。一部のプログラムは、この動作がコマンド ラインフラグで指定されていない場合でも、入力/出力ファイル記述子を自動的にシーク/切り捨てることがあります。これにより、シェルの {{man|3|lseek}} 呼び出しが無効になったり、開いているファイル記述子が予期せず切り詰められたりすることがあります。}}}} |
||
− | {{warning|Avoiding using this approach if you cannot ensure the program which you need it write with offset really utilize {{man|2|splice}} (which implies that the program does not perform any kind of seeking or truncating on its output). Some programs may seek/truncate on input/output file descriptor by themselves even if this behaviour is unspecified on command line flags, which invalidates your shell's {{man|3|lseek}} call, or unexpectedly truncates on opened file descriptor.}}}} |
||
=== VFAT ファイルシステムイメージのボリュームラベルを表示する === |
=== VFAT ファイルシステムイメージのボリュームラベルを表示する === |
2023年5月19日 (金) 18:10時点における版
dd はファイルの変換とコピーを主な目的とする コアユーティリティ です。
cp と同様にデフォルトでは dd はファイルのビットごとのコピーを作成しますが、低レベルの I/O フロー制御機能を備えています。
詳細は、dd(1) またはフルドキュメントを参照してください。
インストール
dd は GNU coreutils の一部です。このパッケージ内の他のユーティリティについては、Core utilities を参照してください。
ディスクの複製と復元
dd コマンドはシンプルでありながら多機能で強力なツールです。ファイルシステムの種類や OS に関係なく、コピー元からコピー先へブロック単位でコピーすることができます。ライブ CD のようなライブ環境から dd を使用するのが便利です。
パーティションの複製
物理ディスク /dev/sda
のパーティション 1 から、物理ディスク /dev/sdb
のパーティション 1 へ:
# dd if=/dev/sda1 of=/dev/sdb1 bs=64K conv=noerror,sync status=progress
ハードディスク全体の複製
物理ディスク /dev/sda
から物理ディスク /dev/sdb
へ:
# dd if=/dev/sda of=/dev/sdb bs=64K conv=noerror,sync status=progress
このコマンドは、パーティションテーブル、ブートローダー、すべてのパーティション、UUID、データを含むドライブ全体を複製します。
bs=
はブロックサイズを設定します。デフォルトは512バイトで、これは1980年代前半以降のハードドライブの「古典」的なブロックサイズですが、最も便利なものではありません。64KBや128KBなど、より大きな値を使用してください。また、「ブロックサイズ」だけでなく、読み取りエラーの伝搬にも影響を与えるため、下記の警告をお読みください。詳細は、[1] と [2] を参照して、最適な bs 値を見つけてください。noerror
はすべての読み取りエラーを無視して操作を続けるように dd に指示します。dd のデフォルトの動作は、いかなるエラーでも停止します。sync
は読み込みエラーがあった場合、入力ブロックをゼロで埋め、データのオフセットは同期されたままになります (読み込みエラーが疑われる場合、sync を用いた際の読み込みエラーの挙動に関する下記の詳細な説明を見てください)。status=progress
は、操作がいつ完了するかを推測するために使用できる転送統計を表示します。
dd ユーティリティには、技術的には「入力ブロックサイズ」(IBS)と「出力ブロックサイズ」(OBS)があります。bs
を設定すると、実質的に IBS と OBS の両方を設定することになります。通常、ブロックサイズが例えば 1MiB の場合、dd は 1024×1024 バイトを読み込み、同じバイト数を書き込みます。しかし、読み取りエラーが発生すると、事態はおかしくなります。多くの人は、noerror,sync
オプションを使うと、dd が「読み込みエラーをゼロで埋める」と思っているようですが、そうではありません。ドキュメントによると、dd は読み込み完了後に OBS から IBS のサイズを埋める、つまりブロックの最後にゼロを追加するのです。つまり、ディスクの場合、512 バイトの読み取りエラーが読み取りの最初に1回発生しただけで、事実上 1MB 全体がめちゃくちゃになってしまうのです: 12ERROR89 は 120000089 ではなく 128900000 となります。
ディスクにエラーがないことが確認できれば、ブロックサイズを大きくしてコピーを進めることができ、コピーの速度が数倍向上します。例えば、Celeron 2.7GHz のシステムで、bs を 512 から 64K に変更すると、コピー速度が 35MB/s から 120MB/s になります。ただし、コピー元のディスクで発生した読み取りエラーは、コピー先のディスクではブロックエラーとして発生することに注意してください。つまり、512バイトのリードエラーは、出力先の 64 KiB ブロック全体をめちゃくちゃにします。
パーティションテーブルのバックアップ
fdisk#パーティションテーブルのバックアップとリストア または gdisk#パーティションテーブルのバックアップとリストア を参照。
ディスクイメージの作成
ライブ環境のメディアから起動し、ソースのハードドライブのパーティションがマウントされていないことを確認してください。
次に、外部のハードドライブをマウントし、ドライブをバックアップしてください:
# dd if=/dev/sda conv=sync,noerror bs=64K | gzip -c > /path/to/backup.img.gz
必要であれば (出力ファイルが FAT32 ファイルシステム上に保存される場合など)、ディスクイメージを複数に分けてください (split(1) も見てください):
# dd if=/dev/sda conv=sync,noerror bs=64K | gzip -c | split -a3 -b2G - /path/to/backup.img.gz
ローカルに十分なディスクスペースがない場合、イメージを ssh を通して送ることもできます:
# dd if=/dev/sda conv=sync,noerror bs=64K | gzip -c | ssh user@local dd of=backup.img.gz
最後に、イメージに保存されているパーティションテーブルを解釈するために必要な、ドライブのジオメトリ情報に関する追加情報を保存します。最も重要なのはシリンダーサイズです。
# fdisk -l /dev/sda > /path/to/list_fdisk.info
{{Tip|gzip はデータ圧縮にシングルの CPU コアしか使用できません。そのせいで、データのスループットが最近のストレージの書き込みスピードよりかなり遅くなってしまいます。マルチコア圧縮でディスクイメージの作成をより速くするには、例えば pigz などのパッケージをインストールし、上記の gzip -c
コマンドを pigz -c
に置き換えてください。巨大なディスクでは時間を節約できるかもしれません。また、他の圧縮アルゴリズム (zstd など) を試しても良いかもしれません。
システムの復元
システムを復元するには:
# gunzip -c /path/to/backup.img.gz | dd of=/dev/sda
イメージが分割されている場合は、代わりに以下を使用してください:
# cat /path/to/backup.img.gz* | gunzip -c | dd of=/dev/sda
MBR のバックアップと復元
ディスクに変更を加える前に、ドライブのパーティションテーブルとパーティションスキームをバックアップしておくと良いでしょう。また、同じパーティションレイアウトを複数のドライブにコピーするためにバックアップを使うこともできます。
MBR はディスクの先頭 512 バイトに格納されています。MBR は4つの部分から成ります:
- 始めの 440 バイトにはブートストラップコード (ブートローダ) が含まれています。
- 次の 6 バイトにはディスクのシグネチャが含まれています。
- 次の 64 バイトにはパーティションテーブルが含まれています (各16バイトの4つのエントリ、各プライマリパーティションに1つのエントリ)。
- 最後の 2 バイトにはブートシグネチャが含まれています。
MBR を mbr_file.img
として保存するには:
# dd if=/dev/sdX of=/path/to/mbr_file.img bs=512 count=1
完全な dd ディスクイメージから MBR を取り出すこともできます:
# dd if=/path/to/disk.img of=/path/to/mbr_file.img bs=512 count=1
バックアップから MBR を復元するには (注意。このコマンドは既存のパーティションテーブルを破壊し、ディスク上のすべてのデータにアクセスできなくなります):
# dd if=/path/to/mbr_file.img of=/dev/sdX bs=512 count=1
ブートローダを復元したいだけで、プライマリパーティションテーブルのエントリに興味はないならば、単に MBR の先頭 440 バイトを復元すれば良いだけです:
# dd if=/path/to/mbr_file.img of=/dev/sdX bs=440 count=1
パーティションテーブルだけを復元するには、以下のコマンドを使う必要があります:
# dd if=/path/to/mbr_file.img of=/dev/sdX bs=1 skip=446 count=64
ブートローダーの削除
MBR ブートスラップコードを消去するには(別のオペレーティングシステムを完全に再インストールする必要がある場合に役立つ場合があります)、最初の440バイトのみをゼロにする必要があります:
# dd if=/dev/zero of=/dev/sdX bs=440 count=1
ディスク関連や他の使用場面
As some readers might already realized, the dd(1) core utility has an quite different command-line syntax compared to other utilities. Moreover, while supporting some unique features not found in other commodity utilities, several default behaviour (and sometimes, inability) it has is either less-ideal or potential error-prone if applied to specific scenario. For that reason, users may want to use some alternatives that better in some aspects in lieu of the dd core utility.
That said, it is still worth to note that since dd is a core utility, which is installed by default on Arch and many other systems, is preferable to some alternatives or more specialized utilities if it is inconvenient to install a new package on your system.
To cover the two aspects that addressed above, this section is dedicated to summarise the features of dd(1) core utility that rarely found in other commodity utilities, in a form that resemble pacman/Rosetta article but with quantities of examples being cut down to as few and simple as possible to just enough to examine these features of dd (as denoted by i.e. or To-clause in "Tip:" box under subsection), either in practice or pseudocode.
バイナリファイルにブロック単位のパッチをインプレースに適用する
自動シェルスクリプトで機能が限定されたバイナリファイルパッチャーとして dd を使用することは珍しいことではありません。これは、書き込み前に指定されたオフセットによって出力ファイルを seek
することができ、ブロックすることができるためです。 conv=notrunc
オプションを追加して、出力ファイルにブロック単位 (または bs=1
の場合はバイト y バイト) でインプレースパッチを適用します。
たとえば、cpio(5) § Portable ASCII Format アーカイブ内の最初のメンバーのタイムスタンプ部分を変更するには、ファイルの 49 番目のバイトから始まります (または、次の場合は 0x30
のオフセットで始まります)。16 進表記を好む場合):
$ touch a-randomly-chosen-file $ bsdtar -cf example-modify-ts.cpio --format odc -- a-randomly-chosen-file
$ printf '%011o' "$(date -d "2019-12-21 00:00:00" +%s)" | dd conv=notrunc of=example-modify-ts.cpio seek=48 oflag=seek_bytes
VFAT ファイルシステムイメージのボリュームラベルを表示する
ファイルシステム VFAT のボリュームラベル イメージファイルを読み取るには、ASCII スペースが埋め込まれた全長 11 バイトで、オフセットは 0x047
です。:
$ truncate -s 33M empty-hole.img $ fatlabel empty-hole.img LabelMe
$ dd iflag=skip_bytes,count_bytes count=11 skip=$((0x047)) if=empty-hole.img | sed -e 's% *$%%'
パイプで繋がれたコマンド間で sponge する
次の例では、出力側のブロックが予想より長くなった場合に、入力側で不必要に長く続く TCP 接続を避けるために、2 つのコマンドの間に dd を挿入し、出力ブロックサイズは入力よりも確実に大きく、それでも使用可能なメモリよりはかなり小さいものにすることができます。
$ curl -qgsfL http://example.org/mirrors/ftp.archlinux.org/mirrored.md5deep | dd ibs=128k obs=200M | poor-mirroring-script-that-perform-mirroring-on-input-paths-line-by-line-wo-buffer-entire-list-first
サイズの制限付きでデータを転送する
データストリーミングのシェルスクリプトで dd を使用すると、パイプコマンドで消費されるデータの合計長を制限することができるのは一般的です。例えば、ustar ヘッダブロック (tar(5) § POSIX ustar Archives) をシェルスクリプトの機能を使ってストリーミング方式で検査する場合です:
hexdump-field() { set -o pipefail printf '%s[%d]:\n' $1 $2 dd count=${2}B status=none | hexdump -e $2'/1 "%3.2x"' -e '" | " '$2'/1 "%_p" "\n"' } inspect-tar-header-block() { local -a hdrstack=( name 100 mode 8 uid 8 gid 8 size 12 mtime 12 checksum 8 typeflag 1 linkname 100 magic 6 version 2 uname 32 gname 32 devmajor 8 devminor 8 prefix 155 pad 12 ) set - ${hdrstack[@]} while test $# -gt 0; do hexdump-field $1 $2 || return shift 2 done }
$ bsdtar -cf - /dev/tty /dev/null 2>&- | dd count=1 skip=1 status=none | inspect-tar-header-block
ブータブルなディスクイメージをブロックデバイスに書き込み、任意で進捗情報を表示する
その場合に最も適合性が低い可能性がある dd を含むコモディティ ユーティリティの例については、USB インストールメディア#基本的なコマンドラインユーティリティを使う を参照してください。
トラブルシューティング
部分読み取り: コピーされたデータは要求されたデータよりも小さい
[3] のように、現時点で完全な入力ブロックが利用できない場合、dd で作成されたファイルは要求されたサイズよりも小さくなる可能性があります。per ドキュメント:
- さらに、データ変換する
conv
被演算子 (つまり、この Wiki 記事のようなオプション) が指定されていない場合、入力は読み取られるとすぐに出力にコピーされます。ブロックサイズより小さいです。
Linuxでは、read(2) システムコールは pipe(7) から読むとき、または /dev/urandom
や /dev/random
などのデバイスファイルを読むときに早く戻ることがあります (つまり、部分読み) (例えば、基礎となるカーネルデバイスドライバーのハードコーディングされた制限 またはエントロピーが不十分です) ここで n
は出力にコピーする入力ブロックの最大数 (潜在的な部分) を制限します。
可能性はありますが、保証はできません。dd がそのような種類の問題について警告する可能性があります。
dd: warning: partial read (X bytes); suggest iflag=fullblock
解決策は、警告にあるように、入力ファイルオプションに加えて iflag=fullblock
オプションを dd コマンドに追加することです。たとえば、合計長が 40 MB のランダム データで満たされた新しいファイルを作成するには、次のようにします。
$ dd if=/dev/urandom of=new-file-filled-by-urandom.bin bs=40M count=1 iflag=fullblock
パイプから読み込む場合、iflag=fullblock
の 代替案 として、bs
を linux/limits.h
で定義されている PIPE_BUF
定数に制限して pipe(7) の I/O を原子化します。例えば、全長5メガバイトのランダムな英数字で埋め尽くされたテキストファイルを用意する場合、以下のようになります:
$ LC_ALL=C tr -dc '[:alnum:]' </dev/urandom | dd of=passtext-5m.txt bs=4k count=1280
出力ファイルはパイプではないため、(入力) パイプと (出力) ディスク上のファイル。たとえば、出力ファイルのより効率的なブロック サイズを設定するには、次のようにします。
$ LC_ALL=C tr -dc '[:alnum:]' </dev/urandom | dd of=passtext-5m.txt ibs=4k obs=64k count=1280
総転送バイト数の読み出しが間違っている
以下の概念実証のように、出力への書き込み時にエラーが発生した場合 (例えば、SIGPIPE、欠陥のある媒体、または誤ってターゲットネットワークブロックデバイスを切断したことによる 部分書き込み)、読み出された総転送バイト数は実際よりも大きくなる可能性があります:第2の dd は明らかに512200バイト以上を読みませんが、第1の dd インスタンスはまだ不正確なバイト数512400バイトを報告します:
$ yes 'x' | dd bs=4096 count=512400B | dd ibs=1 count=512200 status=none >/dev/null 125+1 records in 125+1 records out 512400 bytes (512 kB, 500 KiB) copied, 10.7137 s, 47.8 kB/s
上記の PoC のように中断された転送を再開する場合、"+" 記号の前の数字で示されるように、既にコピーされた全出力ブロックの数の読み出しのみに依存することをお勧めします。