我有以下文件結構:
- 一些目錄
- 一些文件.txt
- 這裡還有另一個檔案.log
- 還有一個文件.mp3
- 另一個目錄
- 與其他一些 file.txt
- 根級別文件.txt
- 根級別的另一個文件.ext
我現在想做的是運行一個小腳本,該腳本將另一個文件作為輸入,其中包含某種類型的模式/替換對,以根據它們遞歸地重命名這些文件。這樣每個“another”(不區分大小寫)都會被“foo”替換,或者每個“some”被“bar”替換。
我已經嘗試了很多迭代文件和讀取所述輸入文件的方法,但是沒有任何效果,我最終成功地意外覆蓋了我的測試腳本。但有很多ls
、while
、sed
或mv
正在使用中。
我自己無法解決的兩件事是如何處理文件名中的空格以及如何不處理在先前的模式匹配中已重命名的文件。
也許你可以給我正確的方向?
答案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,然後將它們放入sh
gulp 中。這最大限度地減少了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
' {} {} \;