Rename
用下劃線替換文件名中的所有點
我有一個目錄,其中包含一些子目錄,其中的文件名和目錄名包含一些點:
$ 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
(儘管ksh93
或zsh
將這樣做),您總是可以這樣做:find . -depth ! -name '.*' -name '*.*' -exec bash -c ' for file do dir=${file%/*} base=${file##*/} mv -i -- "$file" "$dir/${base//./_}" done' sh {} +
(儘管您會錯過由 完成的健全性檢查
zmv
)。那些(故意)不重命名隱藏文件。
重點是處理列表深度優先(
zmv
預設情況下,使用-depth
infind
)並且只重命名文件的基本名稱。這樣你就可以:
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=
(即從IFS
for中刪除所有空白字元read
,否則會將它們從輸入記錄的末尾刪除)。-r
否則read
將反斜杠視為轉義字元(用於欄位和記錄分隔符)。- *與文件描述符有一點關係,*因為您希望標準輸入
mv
是終端(用於-i
提示的答案),而不是來自find
.