ディレクトリ内のすべてのディレクトリにアンダースコア文字のプレフィックスを追加する

ディレクトリ内のすべてのディレクトリにアンダースコア文字のプレフィックスを追加する

ディレクトリ内に複数のディレクトリがあり、名前の先頭にアンダースコア「_」文字を含めるように名前を変更する必要があります。

テストとして、次の 3 つのディレクトリを含む親ディレクトリを作成しました。

dir1, dir2 and dir3; three files: file1, file2, file3

および 3 つのドットプレフィックスファイル:

.file1, .file2, .file3 

これまで、Mac にgfindインストールした後、を使ってみました:findutils

gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

結果は次のようになります:

mv: rename ./. to ./_.: Invalid argument

そこで、find で rename を使ってみました:

find . -type d -exec rename 's/^/_/' {} \;

結果は次のようになります:

Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory

誰か実行可能な解決策を知っていたり、私がどこで失敗しているのか知っている人はいませんか?

答え1

正しく理解していれば、次のようなファイルとディレクトリがあります。

$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1  .file2  .file3  dir1  dir2  dir3  file1  file2  file3

ディレクトリの名前を_dir1_dir2、に変更したいです_dir3か?

それはfindできprintf mvそうです。何が印刷されるか見てみましょう:

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"

最初のコマンドはエラーになります。「ドット」は特殊文字なので名前を変更できません。しかし、他のコマンドは問題なく、希望どおりにディレクトリ名が変更されるはずです。

$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1  _dir2  _dir3

しかし、シェルにコマンドをパイプするのは少々見苦しく、ファイル名が奇妙な場合は、結果は驚くべきものになります (たとえば、$シェルで変数またはコマンドの置換をトリガーする記号が含まれている場合など)。

ファイルがすべて 1 つのレベルにある場合は、次のようにします。

for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done

(ただし、_$xすでに存在する場合は、$xそこに移動されます。)

名前がドットで始まるディレクトリを含める場合は、shopt -s dotglob事前に を使用してください。

これも近いですね:

find . -type d -exec rename 's/^/_/' {} \;

しかし、 はで始まるパスfindを与えるので、それを考慮する必要があります。 1 つのレベルのみで、これは実行できます (先頭のを に変更します)。rename./././_

find . -type d -exec rename 's,^./,./_,' {} \;

すべてのレベルのディレクトリを取得するには、find -execdir使用するのが最も簡単かもしれません。ファイルのディレクトリでコマンドを実行します。-depth名前の変更は正しい順序で処理する必要があります。

find . -depth -type d -execdir rename 's,^./,./_,' {} \;

も追加するといいかもしれません! -name .。例:

$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls  _foo
_dir1  _dir2

答え2

最初の試みの問題は、.すべてのサブディレクトリに加えて、それ自身 (現在のディレクトリ) の名前を変更しようとしていることです。それをスキップしてください。

gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh

しかし、実際には、これを行わないでください。パイプに何かを入力しないでくださいshこれは機能しませんただし、ファイル名にシェルの特殊文字が含まれていない場合に限ります。 というファイルがある場合(これは完全に有効ですが、珍しいファイル名です)、ホーム ディレクトリに移動します。以下に示すよう`rm -r ~`に、シェルを起動します。-exec

_2 回目の試みは、 をベース名の先頭、つまり最後の の後に追加するのではなく、完全なパスの先頭にを挿入しているため機能しません/

find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;

ネストされたディレクトリがある場合、これらのどちらも実際には機能しません。findこれは、ディレクトリをトラバースしながらディレクトリの名前を変更するためです。これを行う場合、オプションを使用して、ディレクトリ自体を操作する前に、ディレクトリの内容を操作する必要があります-depth

find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;

より移植性の高い方法として、シェルを呼び出して を実行しますmv。これはどの POSIX システムでも動作します。

find . -depth -type d -name . -o -exec sh -c '
    for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +

ネストされたサブディレクトリがない場合は、単純なシェル ループを使用できます。名前がドットで始まるディレクトリの名前を変更したくない場合は、*/ディレクトリとディレクトリへのシンボリック リンクのみに一致するワイルドカードを使用します。

for d in */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi   # robustness in case there are no subdirectories
  if [ -L "$d" ]; then continue; fi     # optional: skip symbolic links to directories
  mv -- "$d" "_${d%/}"
done

名前がドットで始まるディレクトリを含むすべてのディレクトリの名前を変更する場合は、.*ワイルドカードも含めます。

for d in .*/ */; do
  if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
  if [ -L "$d" ]; then continue; fi
  mv -- "$d" "_${d%/}"
done

fooに移動するときに_foo、 というディレクトリがすでに存在する場合は_foo、古いディレクトリfooが に移動されることに注意してください_foo/foo。これを防ぐには、明示的なテストを追加します。

または、代わりにzshを使用します。これはmacOSにプリインストールされています。まず、これを実行(またはスクリプトにautoload -U zmv記述)して、.zshrczmv関数、非再帰バージョン(ドットファイルを含むがシンボリックリンクは除く) (/D)は、glob 修飾子):

zmv -Q '*(/D)' '_$f'

または再帰バージョン(修飾子dirname と basename を抽出します):

zmv -Q '**/*(/D)' '$f:h/_$f:t'

zmvターゲットがすでに存在する場合は移動を拒否します。

関連情報