我需要重命名目錄中的多個目錄,以在名稱開頭包含下劃線“_”字元。
作為測試,我創建了一個父目錄,其內容包含三個目錄:
dir1, dir2 and dir3; three files: file1, file2, file3
和三個點前綴檔:
.file1, .file2, .file3
到目前為止,我已經嘗試在 Mac 上gfind
安裝後使用 , :findutils
gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
這將返回:
mv: rename ./. to ./_.: Invalid argument
所以,我嘗試使用重命名和查找:
find . -type d -exec rename 's/^/_/' {} \;
這將返回:
Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory
有人碰巧有一個可行的解決方案或知道我可能會在哪裡犯錯嗎?
答案1
如果我沒猜錯的話,你會有這樣的檔案和目錄:
$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1 .file2 .file3 dir1 dir2 dir3 file1 file2 file3
並想將目錄重命名為_dir1
, _dir2
, _dir3
?
那find
/printf mv
看起來可行。讓我們看看它印了什麼:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"
第一個命令會拋出錯誤,因為“點”很特殊,您無法重新命名它。但其他的應該沒問題,並且應該根據需要重命名目錄:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1 _dir2 _dir3
但是透過管道將命令傳輸到 shell 有點難看,如果檔案名稱足夠奇怪,結果將會令人驚訝(例如,如果它們包含$
將在 shell 中觸發變數或命令替換的符號)。
如果文件都在一個層級上,那麼應該這樣做:
for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done
(儘管如果_$x
已經存在,$x
則會被移入其中。)
如果要包含名稱以點開頭的目錄,請shopt -s dotglob
提前使用。
這也很接近:
find . -type d -exec rename 's/^/_/' {} \;
但由於find
給了rename
以 開頭的路徑./
,我們需要考慮到這一點。僅在一個層面上,這應該可以(將前導更改./
為./_
):
find . -type d -exec rename 's,^./,./_,' {} \;
要取得所有層級的目錄,find -execdir
可能是最容易使用的。它在檔案的目錄中運行命令。我們需要-depth
以正確的順序處理重命名。
find . -depth -type d -execdir rename 's,^./,./_,' {} \;
也許也可以添加! -name .
。例如
$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls _foo
_dir1 _dir2
答案2
您第一次嘗試的問題是,.
除了所有子目錄之外,您還嘗試重命名自身(當前目錄)。跳過它。
gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
但實際上,不要這樣做。切勿將東西透過管道輸入sh
.這不起作用除非您的檔案名稱碰巧不包含任何 shell 特殊字元。如果您有一個名為`rm -r ~`
(完全有效,但不常見的文件名稱)的文件,則 pop 會進入您的主目錄。呼叫 shell,-exec
如下所示。
您的第二次嘗試不起作用,因為您_
在完整路徑的開頭插入 ,而不是在基本名稱的開頭添加它,即在最後一個/
.
find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;
如果您有巢狀目錄,這些實際上都不起作用,因為它們在find
遍歷目錄時會重新命名目錄。執行此操作時,需要使用選項對目錄本身進行操作,然後再對目錄內容進行操作-depth
。
find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;
更方便的是,呼叫 shell 並運行mv
。這適用於任何 POSIX 系統。
find . -depth -type d -name . -o -exec sh -c '
for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +
如果沒有巢狀子目錄,那麼您可以使用簡單的 shell 循環。如果不想重新命名以點開頭的目錄,只需使用通配符*/
,它只會匹配目錄和目錄的符號連結。
for d in */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi # robustness in case there are no subdirectories
if [ -L "$d" ]; then continue; fi # optional: skip symbolic links to directories
mv -- "$d" "_${d%/}"
done
如果要重新命名所有目錄(包括名稱以點開頭的目錄),請.*
同時包含萬用字元。
for d in .*/ */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
if [ -L "$d" ]; then continue; fi
mv -- "$d" "_${d%/}"
done
請注意,當您移動foo
到時_foo
,如果已經有一個名為 的目錄_foo
,則會將舊目錄foo
移至_foo/foo
。為了防止這種情況,請新增明確測試。
或者,也可以使用 zsh。它預先安裝在 macOS 上。第一次運行autoload -U zmv
(將其放入您的.zshrc
或您的腳本中)以加載zmv
功能,那麼非遞歸版本(包括點文件,不包括符號連結 - (/D)
是一組全域限定符):
zmv -Q '*(/D)' '_$f'
或遞迴版本(使用修飾語提取目錄名和基本名):
zmv -Q '**/*(/D)' '$f:h/_$f:t'
zmv
如果目標已經存在,將拒絕採取行動。