Procurando uma ferramenta/script Unix que, com um caminho de entrada, compactará cada lote de arquivos de texto descompactados de 100 MB em um único arquivo gzip

Procurando uma ferramenta/script Unix que, com um caminho de entrada, compactará cada lote de arquivos de texto descompactados de 100 MB em um único arquivo gzip

Eu tenho um despejo de milhares de pequenos arquivos de texto (1-5 MB), cada um contendo linhas de texto. Preciso "agrupar" eles, para que cada lote tenha um tamanho fixo - digamos 100 MB, e compactar esse lote.

Agora esse lote poderia ser:

  1. Um único arquivo que é apenas um 'gato' do conteúdo dos arquivos de texto individuais, ou
  2. Apenas os próprios arquivos de texto individuais

Ressalvas:

  1. unix split -bnão funcionará aqui porque preciso manter as linhas de texto intactas. Usar a linesopção é um pouco complicado porque há uma grande variação no número de bytes em cada linha.
  2. Os arquivos não precisam ter um tamanho estritamente fixo, desde que estejam dentro de 5% do tamanho solicitado
  3. As linhas são críticas e não devem ser perdidas: preciso confirmar se a entrada chegou à saída sem perdas - qual soma de verificação contínua (algo como CRC32, MAS melhor/"mais forte" em face de colisões)

Um script deve funcionar bem, mas parece uma tarefa que alguém já fez antes, e seria bom ver algum código (de preferência python ou ruby) que fizesse pelo menos algo semelhante.

Responder1

O script a seguir criará pacotes compactados de arquivos dentro de um determinado diretório. Você o invoca como:

script.sh directorypath

Ele usa um algoritmo simples/ingênuo (como o que você descreveu):

  • Conte todos os arquivos.
  • Comece a ler todos os arquivos recursivamente.
  • Obtenha tamanho, entrada ls e soma de verificação (SHA1) de cada arquivo. (Armazene a entrada ls em manifest.txt e checksum em checksum.txt).
  • Se o tamanho acabou de ultrapassar o limite, use a lista manifest.txt para criar um tar compactado (que também inclui os arquivos de manifesto e de soma de verificação).
  • Crie um diretório temporário e descompacte o tar recém-criado.
  • Calcule novas somas de verificação de arquivos recém-extraídos.
  • Compare com somas de verificação armazenadas.
  • Se pelo menos uma soma de verificação for diferente, saia com um erro.
  • Caso contrário, verifique se a opção de exclusão está habilitada; em caso afirmativo, remova os arquivos de origem.
  • Repita até que não haja mais arquivos.

Sua saída é como:

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

Os arquivos do pacote são criados no diretório atual.

Um aviso:

  • o diretório atual e o diretório de origem não devem ser pai/filho ou filho/pai, não foi testado dessa forma.

Este é o script (O PACKSIZELIMIT é o número de bytes, se DELETESOURCE for 1, ele excluirá os arquivos de origem [bem, você também deve remover o símbolo # antes da linha "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

Responder2

A divisão GNU tem uma -Copção semelhante, -bmas não quebra linhas.

informação relacionada