
Ich habe einen Dump von Tausenden kleiner Textdateien (1-5 MB) mit jeweils mehreren Textzeilen. Ich muss sie „stapelweise“ zusammenfassen, sodass jeder Stapel eine feste Größe hat – sagen wir 100 MB – und diesen Stapel dann komprimieren.
Dieser Stapel könnte nun wie folgt aussehen:
- Eine einzelne Datei, die lediglich eine Zusammenfassung der Inhalte der einzelnen Textdateien ist, oder
- Nur die einzelnen Textdateien selbst
Vorbehalte:
- Unix
split -b
funktioniert hier nicht, da ich Textzeilen intakt halten muss. Die Verwendung derlines
Option ist etwas kompliziert, da die Anzahl der Bytes in jeder Zeile stark variiert. - Die Dateien müssen nicht unbedingt eine feste Größe haben, solange sie innerhalb von 5 % der angeforderten Größe liegen
- Die Zeilen sind kritisch und sollten nicht verloren gehen: Ich muss bestätigen, dass die Eingabe ohne Verlust zur Ausgabe gelangt ist – welche rollierende Prüfsumme (so etwas wie CRC32, ABER besser/„stärker“ im Hinblick auf Kollisionen)
Ein Skript sollte das auch tun, aber das scheint eine Aufgabe zu sein, die schon jemand gemacht hat, und es wäre schön, Code zu sehen (vorzugsweise Python oder Ruby), der zumindest etwas Ähnliches kann.
Antwort1
Das folgende Skript erstellt komprimierte Dateipakete im angegebenen Verzeichnis. Sie rufen es wie folgt auf:
script.sh directorypath
Es verwendet einen einfachen/naiven Algorithmus (genau wie den, den Sie beschrieben haben):
- Alle Dateien zählen.
- Beginnen Sie mit dem rekursiven Lesen aller Dateien.
- Holen Sie sich Größe, ls-Eintrag und Prüfsumme (SHA1) jeder Datei. (Speichern Sie den ls-Eintrag in manifest.txt und die Prüfsumme in checksum.txt.)
- Wenn die Größe gerade das Limit überschritten hat, verwenden Sie die Liste „manifest.txt“, um eine komprimierte TAR-Datei zu erstellen (die auch die Manifest- und Prüfsummendateien enthält).
- Erstellen Sie ein temporäres Verzeichnis und entpacken Sie die gerade erstellte TAR-Datei.
- Berechnen Sie neue Prüfsummen der gerade extrahierten Dateien.
- Vergleichen Sie mit gespeicherten Prüfsummen.
- Wenn mindestens eine Prüfsumme unterschiedlich ist, beenden Sie das Programm mit einem Fehler.
- Überprüfen Sie andernfalls, ob die Löschoption aktiviert ist. Wenn ja, entfernen Sie die Quelldateien.
- Wiederholen Sie den Vorgang, bis keine Dateien mehr vorhanden sind.
Die Ausgabe sieht wie folgt aus:
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...
Paketdateien werden im aktuellen Verzeichnis erstellt.
Eine Warnung:
- Das aktuelle Verzeichnis und das Quellverzeichnis sollten weder über- noch untergeordnet noch unter- oder übergeordnet sein. Dies wurde nicht auf diese Weise getestet.
Dies ist das Skript (PACKSIZELIMIT ist die Anzahl der Bytes, wenn DELETESOURCE 1 ist, werden die Quelldateien gelöscht [Sie sollten auch das Symbol # vor der Zeile „rm -f“ entfernen]):
#!/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
Antwort2
GNU Split verfügt über eine -C
Option, die ähnlich ist, -b
Zeilen aber nicht umbricht.