100GBシステムに80GBファイルがあり、/root/bigfile
このファイルをアーカイブに保存したいとします。/root/bigarchive.tar
明らかに、アーカイブにファイルを追加しながらファイルを削除する必要があります。だから私の質問は次のようになります
アーカイブにファイルを追加しながら、どのようにファイルを削除できますか?
答え1
単一ファイルの非圧縮tarアーカイブは、ヘッダー、ファイル、およびトレーラーで構成されています。したがって、主な質問は、ファイルの先頭に512バイトのヘッダーを追加する方法です。ヘッダーのみを使用して目的の結果を生成することから始めることができます。
tar cf - bigfile | dd count=1 >bigarchive.tar
次に、ファイルの最初の10Gをコピーします。簡単に言えば、ddが一度に1Gibを読み書きできるとしましょう。
dd count=10 bs=1G if=bigfile >>bigarchive.tar
これで、ソースファイルからコピーされたデータをリリースします。
fallocate --punch-hole -o 0 -l 10GiB bigfile
これはデータを次に置き換えます。足りないファイルシステム空間を占有しないゼロです。この方法を続けて、skip=10
次の項目にaを追加してdd
からfallocate
開始オフセットを増やします-o 10GiB
。最後に、いくつかのヌル文字を追加して最終的なtarファイルを埋めます。
ファイルシステムがそれをサポートしていない場合は、fallocate
同様のことができますが、ファイルの末尾から始めてください。まず、ファイルの最後の10 GBを名前付きファイルにコピーしてから、コマンドを使用して元のpart8
ファイルtruncate
のサイズを小さくします。それぞれ10Gibyteの8つのファイルがあるまで同様の作業を続けます。その後、ヘッダーとをリンクしてpart1
からbigarchive.tar
削除part1
し、リンクしてpart2
削除するなどの操作を実行できます。
答え2
ファイルを削除しても必ずしも考えたようになるわけではありません。これがUNIXに似たシステムでシステムコールが呼び出される理由です。unlink
まさかdelete
。マニュアルページから:
unlink() deletes a name from the filesystem. If that name was the last
link to a file and no processes have the file open, the file is deleted
and the space it was using is made available for reuse.
If the name was the last link to a file but any processes still have
the file open, the file will remain in existence until the last file
descriptor referring to it is closed.
したがって、データコンプレッサー/アーカイバーがファイルからデータを読み取っている間、ファイルはまだ存在し、ファイルシステムのスペースを占有します。
答え3
アーカイブにファイルを追加しながら、どのようにファイルを削除できますか?
文脈を考慮して、私は質問を次のように解釈します。
ディスクからデータを読み込んだ直後に、ファイル全体を読み取る前にディスクからデータを削除して変換されたファイルに十分なスペースを確保するにはどうすればよいですか。
変換は、圧縮、暗号化などのデータで実行したいすべての操作にすることができます。
答えは次のとおりです。
<$file gzip | dd bs=$buffer iflag=fullblock of=$file conv=notrunc
簡単に言うと、データを読み込んでgzip(または必要に応じて)に入れ、出力をバッファリングして書き込んだものよりも読み込んだ方が多いことを確認し、ファイルに書き戻します。以下はよりきれいなバージョンで、実行時の出力を示しています。
cat "$file" \
| pv -cN 'bytes read from file' \
| gzip \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$file" conv=notrunc 2>/dev/null
一行ずつ見てみましょう。
cat "$file"
圧縮したいファイルを読んでください。次の部分pvもファイルを読むことができるので、これはcat(UUOC)の役に立たない使用ですが、私はこれがよりきれいだと思います。
パイプを通してpv
進捗情報を表示します(-cN
「ある種の[c]ursorを使用してください」と言って[N]名を指定します)。
パイプはgzip
明らかに圧縮を行います(stdinから読み込み、stdoutに出力)。
このパイプは他のパイプに接続されていますpv
(パイプラインビュー)。
そのパイプはに入りますdd bs=$buffer iflag=fullblock
。$buffer
変数は50MBと同じ数です。ファイルを安全に処理するためにどのくらいのRAMを割り当てたいのかは関係ありません(データポイントは2 GBファイルに50 MBのバッファで十分です)。iflag=fullblock
パイプを通過する前に読み取る最大バイト数を示しますdd
。$buffer
最初にgzipはヘッダーを作成するため、gzipの出力はこの行dd
に表示されます。次に、dd
入力をより読みやすくするためにパイプする前に、十分なデータがあるまで待ちます。また、圧縮できない部分がある場合、出力ファイルが入力ファイルより大きくなる可能性があります。このバッファは、$buffer
ほとんどのバイトでこれが問題にならないことを保証します。
その後、別のパイプラインビューラインに移動し、最後に出力ラインdd
に移動します。書き込み前に出力ファイルを切り捨て(削除)しないように指示する行が指定されましたof
(出力ファイル)。したがって、500バイトがあり、3バイトを書いた場合、ファイルは(代わりにconv=notrunc
notrunc
dd
A
B
BBBAAAAA...
交換済み通過BBB
)。
私はこの部分を扱っておらず、2>/dev/null
不要です。彼らは単にdd
「完了し、この多くのバイトを記録しました」というメッセージを抑制し、出力を少し整理するだけです。各行の末尾にあるバックスラッシュ()は、\
bashが全体の内容をパイプで互いに接続された1つの大きなコマンドとして扱うことを可能にします。
これは使いやすさのための完全なスクリプトです。興味深いことに、「gz-in-place」というフォルダに入れました。それから私が作った略語であるGZIP:gnu zip in-placeを実現しました。だからここにGZIP.shを紹介します。
#!/usr/bin/env bash
### Settings
# Buffer is how many bytes to buffer before writing back to the original file.
# It is meant to prevent the gzip header from overwriting data, and in case
# there are parts that are uncompressible where the compressor might exceed
# the original filesize. In these cases, the buffer will help prevent damage.
buffer=$((1024*1024*50)) # 50 MiB
# You will need something that can work in stream mode from stdin to stdout.
compressor="gzip"
# For gzip, you might want to pass -9 for better compression. The default is
# (typically?) 6.
compressorargs=""
### End of settings
# FYI I'm aware of the UUOC but it's prettier this way
if [ $# -ne 1 ] || [ "x$1" == "x-h" ] || [ "x$1" == "x--help" ]; then
cat << EOF
Usage: $0 filename
Where 'filename' is the file to compress in-place.
NO GUARANTEES ARE GIVEN THAT THIS WILL WORK!
Only operate on data that you have backups of.
(But you always back up important data anyway, right?)
See the source for more settings, such as buffer size (more is safer) and
compression level.
The only non-standard dependency is pv, though you could take it out
with no adverse effects, other than having no info about progress.
EOF
exit 1;
fi;
b=$(($buffer/1024/1024));
echo "Progressing '$1' with ${b}MiB buffer...";
echo "Note: I have no means of detecting this, but if you see the 'bytes read from";
echo "file' exceed 'bytes written back to file', your file is now garbage.";
echo "";
cat "$1" \
| pv -cN 'bytes read from file' \
| $compressor $compressorargs \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$1" conv=notrunc 2>/dev/null
echo "Done!";
別のバッファラインを追加したい今後dd
gzipを使用すると、バッファラインがフラッシュされたときに書きすぎるのを防ぐことができますが、50MiBバッファと1900MB/dev/urandom
のデータのみがすでに動作しているようです(圧縮解除後にmd5sumが一致します)。私にとっては十分に良い割合です。
もう1つの改善点は、書くことをあまりにも遠くに検出することですが、物事の美しさを奪い、多くの複雑さを作らずにそうする方法がわかりません。その時点で、すべてを正しく実行する完全なPythonプログラムに切り替えることもできます(データの破損を防ぐための安全装置を含む)。
答え4
GNUコマンドを使用している場合は、tar
次のオプションを使用できます--remove-files
。
--ファイル削除
ファイルをアーカイブに追加して削除
tar -cvf files.tar --remove-files my_directory