zsh:如果子資料夾中的檔案具有相同的名稱,則重新命名文件

zsh:如果子資料夾中的檔案具有相同的名稱,則重新命名文件

有以下文件結構:

charlie
├──this-file.foo.1
├──this-file.foo.2
└──this-file.foo.3
foxtrot
├──another-file.foo.1
├──another-file.foo.2
├──another-file.foo.3.bar
├──another-file.foo.4.baz
└──another-file.foo.5.qux
this-file.waldo
another-file.waldo

我想重命名根資料夾中的所有文件,在本例中this-file.waldoanother-file.waldo.如果在任何子資料夾中存在與 .waldo 文件具有相同基本名稱的文件,那麼我想使用包含同名文件的子資料夾的名稱重命名 .waldo 文件,如下所示:

charlie
├──this-file.foo.1
├──this-file.foo.2
└──this-file.foo.3
foxtrot
├──another-file.foo.1
├──another-file.foo.2
├──another-file.foo.3.bar
├──another-file.foo.4.baz
└──another-file.foo.5.qux
charlie.waldo
foxtrot.waldo

當且僅當子資料夾具有共享相同基本名稱的文件時,才必須重新命名該文件,並且必須使用與子資料夾相同的名稱來重新命名該文件。

實現這目標最有效的方法是什麼?

  • 始終存在 .waldo 檔案。
  • 如果沒有共享基本名稱的文件,它應該忽略並繼續下一個文件,直到找到匹配項。
  • 檔案名稱是正常的作業系統檔案名,所以是的,可能存在 &()[]# 等。

答案1

這可能是這樣的:

for f (*.waldo(N)) (){ (($#)) && echo mv -- $f $1:h.waldo; } */$f:r.*(NY1)

echo如果滿意,請刪除(空運行))。

在那裡發現了一些不太常見的 zsh 功能:

  • for f (values) action: for 迴圈的簡寫形式(類似 perl 語法)
  • () { body; } args: 匿名函數
  • $var:r: root 名稱(沒有副檔名的部分,如 csh/vim 中...)
  • $var:h: head (目錄名,如 csh/vim 中)
  • (N): Nullglob glob 限定符:所以如果沒有匹配,它不會報告錯誤:
  • (Y1):在第一個符合 glob 限定詞後停止。

答案2

首先,您取得目前目錄中所有文件的列表

files=(*(.DoN))

然後,你對他們每個人做一些事情:

for file in $files; do
  # get the basename
  bname="${file%%.*}"
  # look for matches in subfolders
  typeset -a matches_in_subfolders
  matches_in_subfolders=*/${bname}*
  # Check whether list of matches is longer than 0
  if [ ${#matches_in_subfolders[@]} -gt 0 ] ; then
    # do your magic renaming here
  fi
done

沒有在這裡為您重命名,因為我不確定如何處理多個發現,但是

mv -- "${file}" "${matches_in_subfolders[0]}${file#*.}"

將重命名file為包含它的第一個資料夾的名稱以及它具有的最長副檔名(如果不是.waldo,但是.foo.waldo,我假設您想要charlie.foo.waldo,而不僅僅是charlie.waldo)。

相關內容