1

У меня есть дамп тысяч маленьких текстовых файлов (1-5 МБ), каждый из которых содержит строки текста. Мне нужно "пакетировать" их, чтобы каждая партия имела фиксированный размер - скажем, 100 МБ, и сжать эту партию.

Теперь эта партия может быть:

  1. Отдельный файл, который является просто «котом» содержимого отдельных текстовых файлов, или
  2. Только отдельные текстовые файлы сами

Предостережения:

  1. Unix split -b здесь не будет работать, так как мне нужно сохранить строки текста без изменений. Использование параметра lines немного сложнее, так как в каждой строке имеется большая разница в количестве байтов.
  2. Файлы не обязательно должны быть фиксированного размера, если они находятся в пределах 5% от запрошенного размера.
  3. Строки являются критическими и не должны быть потеряны: мне нужно подтвердить, что входные данные добрались до выхода без потерь - какая скользящая контрольная сумма (что-то вроде CRC32, НО лучше /"сильнее" перед лицом столкновений)

Сценарий должен работать хорошо, но это похоже на задачу, которую кто-то выполнял ранее, и было бы неплохо увидеть некоторый код (предпочтительно python или ruby), который по крайней мере что-то похожее.

2 ответа2

2

В GNU split есть опция -C которая похожа на -b но не разбивает строки.

1

Следующий скрипт создаст сжатые пакеты файлов в заданном каталоге. Вы вызываете это как:

script.sh directorypath

Он использует простой / наивный алгоритм (как и тот, который вы описали):

  • Посчитайте все файлы.
  • Начните читать все файлы рекурсивно.
  • Получить размер, запись ls и контрольную сумму (SHA1) каждого файла. (Сохраните запись ls в manifest.txt, а контрольную сумму - в контрольной сумме).
  • Если размер только что превысил ограничение, используйте список manifest.txt для создания сжатого tar-файла (который также включает файлы манифеста и контрольной суммы).
  • Создайте временный каталог и распакуйте только что созданный tar.
  • Рассчитайте новые контрольные суммы только что извлеченных файлов.
  • Сравните с сохраненными контрольными суммами.
  • Если хотя бы одна контрольная сумма отличается, выйдите с ошибкой.
  • В противном случае проверьте, включена ли опция удаления, и если да, удалите исходные файлы.
  • Повторяйте, пока нет больше файлов.

Его вывод похож на:

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...

Файлы пакета создаются в текущем каталоге.

Одно предупреждение:

  • текущий каталог и исходный каталог не должны быть родительским / дочерним или дочерним / родительским, так как он не был протестирован.

Это скрипт (PACKSIZELIMIT - это количество байтов, если DELETESOURCE равен 1, тогда он удалит исходные файлы [ну, вам также следует удалить # simbol перед строкой «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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .