大容量ファイルを追加してアーカイブし、同時に削除する方法

大容量ファイルを追加してアーカイブし、同時に削除する方法

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=notruncnotruncddABBBBAAAAA...交換済み通過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!";

別のバッファラインを追加したい今後ddgzipを使用すると、バッファラインがフラッシュされたときに書きすぎるのを防ぐことができますが、50MiBバッファと1900MB/dev/urandomのデータのみがすでに動作しているようです(圧縮解除後にmd5sumが一致します)。私にとっては十分に良い割合です。

もう1つの改善点は、書くことをあまりにも遠くに検出することですが、物事の美しさを奪い、多くの複雑さを作らずにそうする方法がわかりません。その時点で、すべてを正しく実行する完全なPythonプログラムに切り替えることもできます(データの破損を防ぐための安全装置を含む)。

答え4

GNUコマンドを使用している場合は、tar次のオプションを使用できます--remove-files

--ファイル削除

ファイルをアーカイブに追加して削除

tar -cvf files.tar --remove-files my_directory

関連情報