Bash

遞歸列出目錄,首先使用子路徑和葉節點(文件)(用於批量重命名文件名的一部分)?

  • December 6, 2017

在聲明它重複之前,請考慮我出於特定原因需要它:批量重命名(或複製到新名稱)包含文件和目錄名稱中的公共字元串的樹結構。這是一個範例(在 Ubuntu 14.04 上嘗試過,因此使用 GNU 工具):

cd /tmp
mkdir myproj
mkdir -p myproj/myproj_AA/myproj_BB
touch myproj/myproj_AA/myproj_BB/myproj_CC.dat
mkdir myproj/myproj_AA/myproj_DD
touch myproj/myproj_AA/myproj_DD/myproj_EE.dat
mkdir -p myproj/myproj_XX/myproj_YY
touch myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
mkdir -p myproj/myproj_XX/myproj_WW
touch myproj/myproj_XX/myproj_WW/myproj_QQ.dat
tree myproj # to visualise

這個目錄結構tree看起來像這樣:

myproj
├── myproj_AA
│   ├── myproj_BB
│   │   └── myproj_CC.dat
│   └── myproj_DD
│       └── myproj_EE.dat
└── myproj_XX
   ├── myproj_WW
   │   └── myproj_QQ.dat
   └── myproj_YY
       └── myproj_ZZ.dat

6 directories, 4 files

所以,我希望 中的所有條目myproj/,包括myproj它本身,都重命名為myTESTproj而不是myproj(無論它可能作為名稱出現在哪裡)。所以,首先我需要獲得一個相對於目前目錄的相對路徑列表 - 然後我需要對其進行排序,以便最外面的孩子(我認為這相當於具有最長相對路徑名的文件,但不確定)是第一個(因為如果我先重命名/mv 目錄,然後嘗試重命名其中的文件,它可能會使用舊的目錄名稱作為第一個參數,並且由於名稱現在已更改而失敗)。

我知道首先ls -R --group-directories-first myproj/要使用ls遞歸和組目錄,但它的輸出是這樣的:

$ ls -R --group-directories-first myproj/
myproj/:
myproj_AA  myproj_XX

myproj/myproj_AA:
myproj_BB  myproj_DD

myproj/myproj_AA/myproj_BB:
myproj_CC.dat

myproj/myproj_AA/myproj_DD:
myproj_EE.dat

myproj/myproj_XX:
myproj_WW  myproj_YY

myproj/myproj_XX/myproj_WW:
myproj_QQ.dat

myproj/myproj_XX/myproj_YY:
myproj_ZZ.dat

…也就是說,它不是帶有子路徑的簡單列表,我可以輕鬆地提供給while read f; do ...

我最接近的是使用find

$ find myproj/
myproj/
myproj/myproj_AA
myproj/myproj_AA/myproj_DD
myproj/myproj_AA/myproj_DD/myproj_EE.dat
myproj/myproj_AA/myproj_BB
myproj/myproj_AA/myproj_BB/myproj_CC.dat
myproj/myproj_XX
myproj/myproj_XX/myproj_YY
myproj/myproj_XX/myproj_YY/myproj_ZZ.dat
myproj/myproj_XX/myproj_WW
myproj/myproj_XX/myproj_WW/myproj_QQ.dat

所以,在這裡我確實有一個簡單的子路徑列表,但是它首先向葉節點排序根節點 - 我首先需要葉節點。我正在嘗試類似的東西find myproj/ | sort -n,但似乎沒有什麼區別。因此,如果我執行以下操作:

$ find myproj/ | sort -n | while read f; do mv -v $f $(echo $f | sed 's/myproj/myTESTproj/g'); done
‘myproj/’ -> ‘myTESTproj/’
mv: cannot stat ‘myproj/myproj_AA’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB’: No such file or directory
mv: cannot stat ‘myproj/myproj_AA/myproj_BB/myproj_CC.dat’: No such file or directory
...

…然後預期的遞歸重命名立即失敗,因為根節點(目錄)首先被重命名,因此對它的所有進一步引用都是無效的。

那麼,如何首先獲得帶有葉節點的子目錄的正確遞歸列表,以便像這樣在批量重命名中使用它?

如果您的目標只是重命名,那麼在目錄本身之前處理每個目錄的內容還不夠嗎,也就是說,您不需要首先(來自所有目錄)的所有葉子?正是這樣做的。find -depth

$ mkdir -p a/b c/d
$ find -depth
./a/b
./a
./c/d
./c
.

然後您可以使用find -exec和 Bash 重命名文件:

$ find -depth ! -name . -name "*myproj*" -execdir bash -c '
   for f; do mv "$f" "${f/myproj/myTESTproj}" ; done' bash {} +

如果您安裝了 Perl 版本的rename命令(有時稱為prename),這將適用於您

find myproj -depth -name '*myproj*' -exec rename -n 's!(.*)myproj!$1myTESTproj!' {} +

確保任何目錄中的子項列在目錄本身之前的選項-depthfind操作的+後綴允許對指定命令的一次呼叫-exec進行多次插入。{}以降低效率為代價,您可以將其替換為\;.

當你確定它會做你想做的事時,刪除-n或替換它-v

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