Rename

用下劃線替換文件名中的所有點

  • August 11, 2014

我有一個目錄,其中包含一些子目錄,其中的文件名和目錄名包含一些點:

$ ls -R
.:
dir  file.with.dots

./dir:
subdir.with.dots

./dir/subdir.with.dots:
other.file

我真的很難捕捉所有這些文件,find因為我不想重命名目前文件夾.及其..本身。

我還閱讀瞭如何在文件名中用下劃線替換點,使副檔名保持完整,但這並不適合這裡,因為主要問題是文件的遞歸選擇。

我試圖用find和替換點tr

find $(pwd) -depth -name '*.*'|while read f; do mv -iv "$f" '"'$(echo $f|tr '.' '_')'"' ; done

但是如果文件的路徑和文件本身都包含點,這會產生錯誤

如何重命名並用下劃線替換所有出現的點?

zsh

autoload zmv
zmv '(**/)(*.*)' '$1${2//./_}'

否則,如果您可以訪問bash(儘管ksh93zsh將這樣做),您總是可以這樣做:

find . -depth ! -name '.*' -name '*.*' -exec bash -c '
 for file do
   dir=${file%/*}
   base=${file##*/}
   mv -i -- "$file" "$dir/${base//./_}"
 done' sh {} +

(儘管您會錯過由 完成的健全性檢查zmv)。

那些(故意)不重命名隱藏文件。

重點是處理列表深度優先(zmv預設情況下,使用-depthin find)並且只重命名文件的基本名稱。

這樣你就可以:

mv -- a.b/c.d a.b/c_d

然後:_

mv -- a.b a_b

另請注意,對於非 zmv 解決方案,如果您在同一目錄下有一個a.b文件和一個a_b目錄,那麼mv -- a.b a_b將愉快地a.b 移入 a_b/. 為避免這種情況,如果在 GNU 系統上,您可以將-T選項添加到mv.


如我所見,您嘗試編輯答案以使用while read循環。

請注意,您通常應該避免while read循環,即使在這種情況下,無論如何都必須執行每行一個命令。如果您真的想使用一個,那麼正確地使用它是很棘手的,並且涉及 ksh/bash/zshisms:

{
 find . -depth ! -name '.*' -name '*.*' -exec printf '%s\0' {} + |
   while IFS= read -rd '' file; do
     dir=${file%/*}
     base=${file##*/}
     mv -i -- "$file" "$dir/${base//./_}"
   done <&3 3<&-
} 3<&0

(如果您的發現支持它,您可以替換-exec printf '%s\0' {} +為)。-print0

你需要:

  • -print0/-exec printf '%s\0' {} +因為你不能依賴換行符作為分隔符,因為換行符是文件名中的有效字元
  • 因此,您需要-d ''read讀取直到 NUL 而不是 NL)
  • IFS=(即從IFSfor中刪除所有空白字元read,否則會將它們從輸入記錄的末尾刪除)。
  • -r否則read將反斜杠視為轉義字元(用於欄位和記錄分隔符)。
  • *與文件描述符有一點關係,*因為您希望標準輸入mv是終端(用於-i提示的答案),而不是來自find.

引用自:https://unix.stackexchange.com/questions/149563