
数千の小さなテキスト ファイル (1 ~ 5 MB) のダンプがあり、それぞれにテキスト行が含まれています。これらのファイルを「バッチ」にまとめ、各バッチのサイズが固定 (たとえば 100 MB) になるようにして、バッチを圧縮する必要があります。
このバッチは次のようになります。
- 個々のテキストファイルの内容を「cat」しただけの単一のファイル、または
- 個々のテキストファイル自体
注意:
split -b
テキスト行をそのまま維持する必要があるため、unix はここでは機能しません。lines
各行のバイト数に大きなばらつきがあるため、このオプションの使用は少し複雑です。- ファイルのサイズは厳密に固定されている必要はなく、要求されたサイズの5%以内であればよい。
- これらの行は重要であり、失われてはいけません。入力が損失なく出力されたことを確認する必要があります。ローリング チェックサム (CRC32 のようなものですが、衝突に対してより優れた/「強力な」もの) が必要です。
スクリプトでも十分機能するはずですが、これは誰かが以前に実行したタスクのようです。少なくとも同様のことを実行するコード (できれば Python または Ruby) があれば便利です。
答え1
次のスクリプトは、指定されたディレクトリ内にファイルの圧縮パッケージを作成します。次のように呼び出します:
script.sh directorypath
シンプル/素朴なアルゴリズムを使用します (説明したものと同じ)。
- すべてのファイルをカウントします。
- すべてのファイルを再帰的に読み取りを開始します。
- 各ファイルのサイズ、ls エントリ、チェックサム (SHA1) を取得します (ls エントリを manifest.txt に保存し、チェックサムを checksum.txt に保存します)。
- サイズが制限を超えた場合は、manifest.txt リストを使用して圧縮された tar (マニフェスト ファイルとチェックサム ファイルも含まれます) を作成します。
- 一時ディレクトリを作成し、作成した tar を解凍します。
- 抽出されたファイルの新しいチェックサムを計算します。
- 保存されたチェックサムと比較します。
- 少なくとも 1 つのチェックサムが異なる場合は、エラーで終了します。
- それ以外の場合は、削除オプションが有効になっているかどうかを確認し、有効になっている場合はソース ファイルを削除します。
- ファイルがなくなるまで繰り返します。
出力は次のようになります。
Reading files...
15898 849 ../out/f068715p.jpg
Creating package (pack18.tar.gz) with 849 files, using 100078420 bytes...
tar: Removing leading `../' from member names
Preparing to verify package...
Creating new checksums...
Comparing checksums...
Package verification OK.
Deleting temporary verification directory...
Reading files...
16731 833 ../out/f069111c.jpg
Creating package (pack19.tar.gz) with 833 files, using 100004735 bytes...
tar: Removing leading `../' from member names
Preparing to verify package...
Creating new checksums...
Comparing checksums...
Package verification OK.
Deleting temporary verification directory...
Reading files...
パッケージ ファイルは現在のディレクトリに作成されます。
警告の1つ:
- 現在のディレクトリとソース ディレクトリは親/子または子/親であってはなりません。そのようにテストされていません。
これはスクリプトです (PACKSIZELIMIT はバイト数です。DELETESOURCE が 1 の場合はソース ファイルが削除されます [また、"rm -f" 行の前の # シンボルも削除する必要があります]):
#!/bin/bash
PACKSIZELIMIT=100000000
PACKPREFFIX="pack"
#PACKSUFFIX=$(date +"_%Y%m%d_%H%M")
PACKSUFFIX=""
DELETESOURCE=0
LISTFILE="packbatchlist.txt"
MANIFESTFILE="manifest.txt"
CHECKSUMFILE="checksums.txt"
VERIFYFILE="verifysums.txt"
if [ -d "$1" ]; then
PACKCOUNTER=0
PACKSIZE=0
FILECOUNTER=0
ALLFILECOUNTER=0
cat /dev/null > $LISTFILE
cat /dev/null > $MANIFESTFILE
cat /dev/null > $CHECKSUMFILE
cat /dev/null > $VERIFYFILE
echo "Reading files..."
TOTALFILES=$(find "$1" -type f | wc -l)
echo "There are $TOTALFILES files to process..."
find "$1" -type f | while read SOURCEFILE ; do
let "FILECOUNTER+=1"
let "ALLFILECOUNTER+=1"
echo -ne "\r$ALLFILECOUNTER $FILECOUNTER $SOURCEFILE\e[K"
THISFILESIZE=$(stat -c %s "$SOURCEFILE")
let "PACKSIZE+=$THISFILESIZE"
echo $SOURCEFILE >> $LISTFILE
ls -l $SOURCEFILE >> $MANIFESTFILE
sha1sum $SOURCEFILE >> $CHECKSUMFILE
if [ $PACKSIZE -gt $PACKSIZELIMIT -o $ALLFILECOUNTER -eq $TOTALFILES ]; then
echo
echo $MANIFESTFILE >> $LISTFILE
echo $CHECKSUMFILE >> $LISTFILE
PACKFILENAME="$PACKPREFFIX$PACKCOUNTER$PACKSUFFIX.tar.gz"
echo "Creating package ($PACKFILENAME) with $FILECOUNTER files, using $PACKSIZE bytes..."
tar -cf - -T $LISTFILE | gzip -c > $PACKFILENAME
echo "Preparing to verify package..."
TEMPCHECKDIR=$(mktemp -d)
tar xzf $PACKFILENAME -C $TEMPCHECKDIR
if [ -r "$TEMPCHECKDIR/$CHECKSUMFILE" ] ; then
cut -d " " -f 1 $TEMPCHECKDIR/$CHECKSUMFILE > $VERIFYFILE
sort $VERIFYFILE > $TEMPCHECKDIR/$CHECKSUMFILE
echo "Creating new checksums..."
cat /dev/null > $VERIFYFILE
find "$TEMPCHECKDIR" -type f | while read CHECKEDFILE ; do
CHECKEDFILESHORT=$(basename $CHECKEDFILE)
if [ "$CHECKEDFILESHORT" != "$MANIFESTFILE" -a "$CHECKEDFILESHORT" != "$CHECKSUMFILE" ] ; then
sha1sum $CHECKEDFILE | cut -d " " -f 1 >> $VERIFYFILE
fi
done
sort $VERIFYFILE -o $VERIFYFILE
echo "Comparing checksums..."
DIFFFILES=$(comm --nocheck-order -3 $TEMPCHECKDIR/$CHECKSUMFILE $VERIFYFILE | wc -l)
if [ $DIFFFILES -gt 0 ] ; then
echo "ERROR: Package failed verification!"
exit 2
else
echo "Package verification OK."
echo "Deleting temporary verification directory..."
find "$TEMPCHECKDIR" -delete
if [ "$DELETESOURCE" == "1" ] ; then
echo "Deleting source files..."
cat $LISTFILE | while read FILE2DEL ; do
echo -ne "\rDeleting $FILE2DEL ... \e[K"
#rm -f $FILE2DEL
done
echo -e "\rFiles deleted.\e[K"
fi
fi
else
echo "ERROR: Cannot find checksum file from package!"
exit 1
fi
let "PACKCOUNTER+=1"
PACKSIZE=0
FILECOUNTER=0
cat /dev/null > $LISTFILE
cat /dev/null > $MANIFESTFILE
cat /dev/null > $CHECKSUMFILE
cat /dev/null > $VERIFYFILE
echo "Reading files..."
fi
done
else
echo
echo "Missing source directory"
echo
fi
rm -f $LISTFILE
rm -f $MANIFESTFILE
rm -f $CHECKSUMFILE
rm -f $VERIFYFILE
答え2
GNU split には、-C
これに似ています-b
が行を分割しないオプションがあります。