尋找 Unix 工具/腳本,在給定輸入路徑的情況下,將每批未壓縮的 100MB 文字檔案壓縮為單一 gzip 檔案

尋找 Unix 工具/腳本,在給定輸入路徑的情況下,將每批未壓縮的 100MB 文字檔案壓縮為單一 gzip 檔案

我有數千個小文字檔案(1-5MB)的轉儲,每個文件都包含文字行。我需要對它們進行“批處理”,以便每個批次都有固定的大小 - 例如說 100MB,然後壓縮該批次。

現在該批次可能是:

  1. 單一文件,只是各個文字文件內容的“貓”,或者
  2. 只是各個文字檔本身

注意事項:

  1. unixsplit -b在這裡不起作用,因為我需要保持文字行完整。使用該lines選項有點複雜,因為每行中的位元組數有很大差異。
  2. 文件大小不必嚴格固定,只要在請求大小的 5% 以內即可
  3. 這些行很關鍵,不應該丟失:我需要確認輸入是否已無丟失地輸出 - 什麼滾動校驗和(類似於 CRC32,但在面對衝突時更好/“更強”)

腳本應該做得很好,但這似乎是有人以前做過的任務,很高興看到一些程式碼(最好是 python 或 ruby​​)至少可以完成類似的事情。

答案1

以下腳本將在給定目錄中建立檔案壓縮包。您可以像這樣調用它:

script.sh directorypath

它使用一種簡單/樸素的演算法(就像您所描述的那樣):

  • 統計所有文件。
  • 開始遞歸讀取所有文件。
  • 取得每個檔案的大小、ls 條目和校驗和 (SHA1)。 (將 ls 條目儲存在 manifest.txt 中,將校驗和儲存在 checksum.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 那麼它將刪除原始檔案[好吧,您還應該刪除“rm -f”行之前的 # simbol]):

#!/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但不換行的選項。

相關內容