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

1

Eu tenho um despejo de milhares de pequenos arquivos de texto (1-5MB) grandes, cada um contendo linhas de texto. Preciso "fazer o batch" deles, para que cada lote tenha um tamanho fixo - digamos, 100MB e comprima esse lote.

Agora esse lote pode ser:

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

Advertências:

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

Um script deve funcionar muito bem, mas isso parece uma tarefa que alguém já fez antes, e seria bom ver algum código (preferencialmente python ou ruby) que faça pelo menos algo similar.

    
por newToFlume 03.07.2012 / 23:24

2 respostas

1

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

script.sh directorypath

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

  • Conte todos os arquivos.
  • Comece a ler todos os arquivos de forma recursiva.
  • Obtenha o tamanho, a entrada ls e a soma de verificação (SHA1) de cada arquivo. (Armazene a entrada ls no manifest.txt e a soma de verificação no checksum.txt).
  • Se o tamanho acabou de passar do limite, use a lista manifest.txt para criar um tar compactado (que também inclui os arquivos manifest e checksum).
  • Crie um diretório temporário e descompacte apenas o tar criado.
  • Calcule novas somas de verificação dos arquivos extraídos.
  • Compare com as 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á ativada. Em caso positivo, remova os arquivos de origem.
  • Repetir até não haver 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, ele não foi testado dessa maneira.

Este é o script (O PACKSIZELIMIT é o número de bytes, se DELETESOURCE for 1, então ele excluirá arquivos de origem [bem, você também deve remover o # simbol 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
    
por 04.07.2012 / 04:44
2

A divisão do GNU tem uma opção -C , que é como -b , mas não quebra linhas.

    
por 04.07.2012 / 05:04