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:rroot名(csh / vimなどの拡張子のない部分...)
  • $var:hhead(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.waldocharlie.waldo

関連情報