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.waldoと ですanother-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 ファイルは常に存在します。
  • ベース名を共有するファイルがない場合、無視して、一致するものが見つかるまで次のファイルに進みます。
  • ファイル名は通常の OS ファイル名なので、&()[]# などが存在する可能性があります。

答え1

それは次のようになります:

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

(echo満足したら (dry-run) を削除します)。

そこには、あまり一般的ではない zsh の機能のいくつかが見つかります。

  • for f (values) action: for ループの短縮形 (Perl 構文に似ています)
  • () { body; } args: 匿名関数
  • $var:r: root 名 (csh/vim のような拡張子のない部分)
  • $var:h: head (dirname、csh/vim と同様)
  • (N): Nullglob glob 修飾子: 一致するものがない場合でもエラーは報告されません:
  • (Y1): 最初の一致グロブ修飾子の後に停止します。

答え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

関連情報