使用模式和替換列表遞歸地重命名文件

使用模式和替換列表遞歸地重命名文件

我有以下文件結構:

  • 一些目錄
    • 一些文件.txt
    • 這裡還有另一個檔案.log
    • 還有一個文件.mp3
  • 另一個目錄
    • 與其他一些 file.txt
  • 根級別文件.txt
  • 根級別的另一個文件.ext

我現在想做的是運行一個小腳本,該腳本將另一個文件作為輸入,其中包含某種類型的模式/替換對,以根據它們遞歸地重命名這些文件。這樣每個“another”(不區分大小寫)都會被“foo”替換,或者每個“some”被“bar”替換。

我已經嘗試了很多迭代文件和讀取所述輸入文件的方法,但是沒有任何效果,我最終成功地意外覆蓋了我的測試腳本。但有很多lswhilesedmv正在使用中。

我自己無法解決的兩件事是如何處理文件名中的空格以及如何不處理在先前的模式匹配中已重命名的文件。

也許你可以給我正確的方向?

答案1

TOP="`pwd -P`" \
find . -type d -exec sh -c '
   for d
   do
      cd "$d" && \
         find . ! -name . -prune -type f -exec sh -c '\''
            while IFS=\; read -r pat repl
            do
               rename "s/$pat/$repl/g" "$@"
               N=$#
               for unmoved
               do
                  if [ -f "$unmoved" ]
                  then
                     set X ${1+"$@"} "$unmoved"
                     shift
                  fi
               done
               shift "$N"
               case $# in 0 ) break ;; esac
            done < patterns.csv
         '\'' x \{\} +
      cd "$TOP"
   done
' x {} +
  • 僅設定find網路目錄併吞下sh它們。這最大限度地減少了 的呼叫次數sh
  • find在每個目錄中設定網路regular文件,深度等級僅為 1,然後將它們放入shgulp 中。這最大限度地減少了rename實用程式被呼叫的次數。
  • 設定一個while循環來讀入各個pattern <-> replacement對並將它們應用於所有regular文件。
  • 在過程中rename-我們會記錄rename處理後文件是否仍然存在。如果我們發現一個檔案仍然存在,那麼這意味著,由於某種原因,它無法重新命名,因此將在下一次迭代中嘗試pat/repl。 OTOH,如果檔案已成功重命名,那麼我們不會pat/repl透過將其從命令列參數列表中刪除來對該檔案應用下一次迭代。

答案2

rPairs="/tmp/rename_pairs" \
find . -type f -exec sh -c '
   while read -r old new; do
      rename "s/$old/$new/i" "$@"
   done < "$rPairs"
' x {} +

假設您的重命名對檔案中沒有非 ASCII 字符,並且該檔案遠離搜尋路徑。

答案3

在拉克什夏爾馬(Rakesh Sharma)的回答之後,我在進行了更多嘗試並睡了一會兒後找到了正確的方向。

最後我想出了以下腳本:

#!/bin/bash


while IFS=";" read pattern replacement
do
  if [[ ! -z $pattern ]]
  then
    echo "Checking files for pattern '$pattern'."

    find ./files -name "*$pattern*" -type f | while read fpath
    do
      fname=$(basename "$fpath")
      dname=$(dirname "$fpath")

      echo "  Found file '$fname' in directory '$dname'. Renaming to '${fname/$pattern/$replacement}'."
      mv -- "$fpath" "$dname/${fname/$pattern/$replacement}"
    done
  fi
done < patterns.csv

它讀取檔案並循環遍歷其填充和變數pattern.csv的行。必須這樣做以避免在第二個模式匹配時再次嘗試重命名文件,因為這會失敗。最後,它僅重命名檔案本身,而不是使用 shell 參數替換來重新命名包含該檔案的目錄。$pattern$replacement./files

不起作用的是替換不區分大小寫的匹配項,但我可以忍受。

答案4

要記住的重要一點是,遍歷目錄樹是一個緩慢的過程,因此只執行一次。我們首先要做的是find只查看樹中的目錄。對於每個目錄,我們尋找regular files它們下面的所有目錄(這裡沒有遞歸)。然後,我們對這些檔案名稱套用重命名轉換,同時記錄它是否成功。如果成功,那麼我們將跳出 while 循環,從而阻止下一個 patt/repl 應用於該檔案。

tempd="`mktemp -d`" \
find . -type d -exec sh -c '
   cd "$1" && \
   for f in ./*
   do
      [ -f "$f" ] || continue
      while IFS=\; read -r patt repl
      do
         case $f in
            ./*"$patt"* )
               rename -v "s/$patt/$repl/g" "$f" 2>&1 | tee "$tempd/$f"
               case $(< "$tempf/$f") in "$f renamed "* ) break ;; esac ;;
         esac
      done < /tmp/patterns.csv
   done
' {} {} \;

相關內容