フォルダ内のすべてのファイルを同じ名前のサブディレクトリに移動するにはどうすればいいですか?

フォルダ内のすべてのファイルを同じ名前のサブディレクトリに移動するにはどうすればいいですか?

ディレクトリにたくさんのファイルがあるとします

filename-1.ext1
filename-1.ext2
filename-1.ext3
filename-2.ext1
filename-2.ext2
filename-2.ext3
filename-3.ext1
filename-3.ext2
filename-3.ext3

次に、同じディレクトリにファイルと同じ名前(ファイル名は拡張子なし)のサブディレクトリを作成し、次のように移動します。

filename-1
├──filename-1.ext1
├──filename-1.ext2
└──filename-1.ext3
filename-2
├──filename-2.ext1
├──filename-2.ext2
└──filename-2.ext3
filename-3
├──filename-3.ext1
├──filename-3.ext2
└──filename-3.ext3

これを達成するための最も効率的な方法は何でしょうか?

編集: ext1、ext2、ext3 は、.foo、.bar、.top、.coffee などの 3 つの異なるファイル名を示すだけです。ファイル名が似ているということではありません。

答え1

を使用しているのでzsh、次のように実行できます。

for name (*.ext<->(.)) mkdir -p -- $name:r && mv -- $name $name:r 

または、より一般的な長い形式のループを使用するとfor

for name in *.ext<->(.); do
    mkdir -p -- $name:r &&
    mv -- $name $name:r
done

*.ext<->これは、任意の番号に一致するパターンに一致するすべての通常ファイルを反復処理します。通常ファイルのみの一致を強制するのは、<->glob 修飾子です。ドットを含む通常ファイルのファイル名に一致させたい場合は、パターン全体を に変更します。後でファイル名拡張子を削除する必要があるため、ドットが存在する必要があります。緩いパターンではなく、より厳密なパターンを使用するには、 のようなものを使用します。このパターンでは、文字列 で始まる名前に一致させる必要があることを知っている必要があります。(.)*.*(.)filename-<->.ext<->(.)filename-

$name:r展開すると、 と同じ値になりますが、拡張子は含まれ$nameません (つまり、値の「ルート」のみになります)。

--両方に対してオプションの終了を通知しmkdirmvダッシュで始まる名前がオプションと間違われるのを防ぐために使用します。

答え2

zmv

autoload -Uz zmv # best in ~/.zshrc
mkmv() { mkdir -p -- $2:h && mv -- "$@"; }

zmv -P mkmv -n '(*).ext<->(#q.)' '$1/$f'

-n満足したら削除してください)。

そうすれば、zmvの健全性チェックの恩恵を受けることができます。

答え3

forbash シェルと zsh シェルの両方と互換性のある次のループを使用します。

for file in *.*; do
  [ -f "$file" ] &&
    mkdir -p -- "${file%.*}" &&
    mv -- "$file" "${file%.*}/"
done
  • -pオプションでmkdirファイルが存在することを確認するとエラーは発生しません
  • %.*ファイル名から拡張子を削除します。

答え4

GNU parallel (その威力に気づき始めたばかりのツール) を使用すると、次のようになります。

ls | parallel 'mkdir -p {.}; mv -i {} {.}'

これは CPU に縛られるものではないため、parallel速度上の利点はないと思われますが、それでも他の多くのソリューションよりも短くなります。

これはファイル名のパターンに依存しません。

関連情報