再帰的なファイルとディレクトリの操作

再帰的なファイルとディレクトリの操作

ファイルを含む複数のディレクトリに対して一連の操作を繰り返し実行しようとしています。各ディレクトリ内に2つのディレクトリを作成し、いくつかのファイルを各ディレクトリに移動したいと思います。

たとえば、私のディレクトリ構造は次のようになります。

   root --
     subdirectory1 --
         file_a1_a.tif
         file_a2_a.tif
         file_a1_b.tif
         file_a2_b.tif
     subdirectory2 --
         file_b1_a.tif
         file_b2_a.tif
         file_b1_b.tif
         file_b2_b.tif

ファイルをこのように整理したいのですが…

   root --
     subdirectory1 --
         subdirectory_a --
             file_a1_a.tif
             file_a2_a.tif
         subdirectory_b --
             file_a1_b.tif
             file_a2_b.tif
     subdirectory2 --
         subdirectory_a --
             file_b1_a.tif
             file_b2_a.tif
         subdirectory_b --
             file_b1_b.tif
             file_b2_b.tif

私は次のことをしていますが、トップレベルのディレクトリでのみ動作しているようです。

find . -type d -execdir bash -c "mkdir -p subdirectory_a && mkdir -p subdirectory_b && mv *_a.tif subdirectory_a 2>/dev/null && mv *_b.tif subdirectory_b 2>/dev/null" \;

どんな助けでも大変感謝します。

答え1

root子だけがあり、孫はいないので、ディレクトリから次を実行できますroot

subA='subdirectory_a'
subB='subdirectory_b'
for d in */; do
    mkdir -p "$d$subA" "$d$subB"
    mv "$d"*_a.tif "$d$subA"
    mv "$d"*_b.tif "$d$subB"
done

$dand で拡張される for ループの glob のため、subdirectory1/直接サブディレクトリにのみ一致します。その孫ファイル(すでに存在する場合は文句を言わないように存在)を作成し、ワイルドカードを再使用してファイルを孫ファイルに移動します。subdirectory2/*/rootmkdir-p

シェルがglobを拡張したい場合は、引用符を解除する必要があります。だからそうですが、mv "$d"*_a.tif "$d$subA"正確mv "$d*_a.tif" "$d$subA"ではありません。

答え2

質問に投稿したコマンド

find . -type d -execdir bash -c "mkdir -p subdirectory_a && mkdir -p subdirectory_b && mv *_a.tif subdirectory_a 2>/dev/null && mv *_b.tif subdirectory_b 2>/dev/null" \;

正しく機能します(つまり、やるべきことを行います)。しかし、それが表現するのはあなたが望むものではありません。

このパラメータは、「...指定されたコマンドは-execdir一致するファイルを含むサブディレクトリで実行されます」(ソースコード、
検索(1))

つまり、現在のジョブが直接であり、rootコマンドがそれをターゲットにしているsubdirectory1場合、期待どおりにドロップダウンされません。内部でsubdirectory1一致するものが-type d見つかったので、その場所にとどまり、そこで指定されたコマンドを実行します。もちろん、その中にファイルがないので何も移動されませんが、また次のようなエラーが発生しないことにも注意してください。findrootmkdirrootmkdir -p目次(1): 「存在する場合、エラーはありません。」


それでは、このゲームをあなたが望むゲームにするにはどうすればよいですか?同じ構造を投稿したとしましょう。

$ tree /tmp/root
/tmp/root
├── subdir1
│   ├── file_a1_a.tif
│   ├── file_a1_b.tif
│   ├── file_a2_a.tif
│   └── file_a2_b.tif
└── subdir2
    ├── file_b1_a.tif
    ├── file_b1_b.tif
    ├── file_b2_a.tif
    └── file_b2_b.tif

3つの段階に分けることをお勧めします。すべてのタスクを実行するために1つのコマンドを使用する必要はありません。 「コードゴルフ」ではなく、目的の結果に集中してください。だからまずディレクトリを作成してください

$ find /tmp/root -maxdepth 1 -mindepth 1 -exec bash -c 'mkdir ${1}/subdirectory_{a,b}' sh  {} \;
$ tree
.
├── subdir1
│   ├── file_a1_a.tif
│   ├── file_a1_b.tif
│   ├── file_a2_a.tif
│   ├── file_a2_b.tif
│   ├── subdirectory_a
│   └── subdirectory_b
└── subdir2
    ├── file_b1_a.tif
    ├── file_b1_b.tif
    ├── file_b2_a.tif
    ├── file_b2_b.tif
    ├── subdirectory_a
    └── subdirectory_b

6 directories, 8 files

次に、ディレクトリツリーの特定のレベルで通常のファイルを見つけて移動します。今、私たちはそれを使用して-execdir私たちが望むものを達成することができます

$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) echo cp ${1} ./subdirectory_a ;; *b.tif) echo cp ${1} subdirectory_b ;; esac' sh {} \;
cp ./file_b2_b.tif subdirectory_b
cp ./file_b2_a.tif ./subdirectory_a
cp ./file_b1_a.tif ./subdirectory_a
cp ./file_b1_b.tif subdirectory_b
cp ./file_a1_b.tif subdirectory_b
cp ./file_a1_a.tif ./subdirectory_a
cp ./file_a2_b.tif subdirectory_b
cp ./file_a2_a.tif ./subdirectory_a

この場合は、移動する前にエコーを使用してすべてが正しく機能していることを確認し、実際の移動を行わないことに注意してください。したがって、それを削除するとecho正しく機能します。

$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) cp ${1} ./subdirectory_a ;; *b.tif) cp ${1} subdirectory_b ;; esac' sh {} \;
$ tree /tmp/root
/tmp/root
├── subdir1
│   ├── file_a1_a.tif
│   ├── file_a1_b.tif
│   ├── file_a2_a.tif
│   ├── file_a2_b.tif
│   ├── subdirectory_a
│   │   ├── file_a1_a.tif
│   │   └── file_a2_a.tif
│   └── subdirectory_b
│       ├── file_a1_b.tif
│       └── file_a2_b.tif
└── subdir2
    ├── file_b1_a.tif
    ├── file_b1_b.tif
    ├── file_b2_a.tif
    ├── file_b2_b.tif
    ├── subdirectory_a
    │   ├── file_b1_a.tif
    │   └── file_b2_a.tif
    └── subdirectory_b
        ├── file_b1_b.tif
        └── file_b2_b.tif

6 directories, 16 files

もちろん、これはデータの損失を防ぐためにレプリケーションを使用することに注意してください。我々はそれをより整理することができ、最終的に希望のディレクトリツリー構造を取得します。

$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -delete
$ tree 
.
├── subdir1
│   ├── subdirectory_a
│   │   ├── file_a1_a.tif
│   │   └── file_a2_a.tif
│   └── subdirectory_b
│       ├── file_a1_b.tif
│       └── file_a2_b.tif
└── subdir2
    ├── subdirectory_a
    │   ├── file_b1_a.tif
    │   └── file_b2_a.tif
    └── subdirectory_b
        ├── file_b1_b.tif
        └── file_b2_b.tif

6 directories, 8 files

簡単に言えば、私たちがすることはスクリプトで表現できます。

find /tmp/root -maxdepth 1 -mindepth 1 -exec bash -c 'mkdir ${1}/subdirectory_{a,b}' sh  {} \; &&
find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) cp ${1} ./subdirectory_a ;; *b.tif) cp ${1} subdirectory_b ;; esac' sh {} \; &&
find /tmp/root -maxdepth 2 -mindepth 2 -type f -delete

もちろん、代わりにmv2段階と3段階を1つに圧縮することはできますが、cpデータ損失のリスクを減らすのではなく、注意してください。

答え3

ディレクトリ内で実行すると、コマンドはroot最上位subdirectoy_aレベルに、およびおよびで終わるすべてのファイルとで終わるすべてのファイルを移動します。これは望むものではありません。これにより、リダイレクトされたエラーがなく、実際にエラーを表示できる場合は、トラブルシューティングが簡単になります。親ディレクトリがすでに存在するため、オプションは必要ありません。subdirectory_bsubdirectory1subdirectory2a.tiffsubdirectory1b.tiffsubdirectory_b/dev/null-pmkdir

持っているのではなく、root内部(または実際の呼び出しディレクトリ)で実行してください。

mkdir subdirectory1/subdirectory_a subdirectory1/subdirectory_b subdirectory2/subdirectory_a subdirectory2/subdirectory_b && mv subdirectory1/file_a*a*  subdirectory1/subdirectory_a && mv subdirectory1/file_a*b*  subdirectory1/subdirectory_b && mv subdirectory2/file_b*a*  subdirectory2/subdirectory_a && mv subdirectory2/file_b*b*  subdirectory2/subdirectory_b

答え4

そしてzsh

cd root || exit
subdirs=(*(-/))
mkdir -p -- $^subdirs/subdirectory_{a,b} || exit
autoload -Uz zmv
zmv '(*/)*_(a|b).tif' '${1}subdirectory_$2/$f:t'

必要なら再帰的subdir1/subsubdir1*あなたの質問からわかるように(あなたの例では提案されていません*(-/)(*/)**

多数のファイルがあり、速度が問題の場合は、zsh組み込みコマンドを実行して(によって呼び出されますzmodload zsh/files)。mkdirmvzmv

一つのために試運転zmvなんだが教えてくれるところ会議オプションを追加してください-n

それぞれを含むディレクトリにおよびを作成したい場合は、subdirectory_a他のアプローチがあります。subdirectory_b*_a.tif*_b.tif

mkmv() { mkdir -p -- $2:h && mv -- "$@"; }
zmv -P mkmv '(**/)*_(a|b).tif' '${1}subdirectory_$2/$f:t'

(ファイルは自分tifrootファイルも移動します。)

関連情報