web-dev-qa-db-ja.com

フォルダー名に基づいて不明なファイルを不明なディレクトリに移動する方法は?

フォルダーのディレクトリを検索し、フォルダーの内容をその場所から1つ上のディレクトリに移動するスクリプトを記述しようとしています。これは私がこれまでに持っているものです:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

私の問題は..の宛先ディレクトリにあると思います。おそらく私はこれを間違って表現していますか?

任意のヘルプが便利です。

* edit**

この病気を簡素化するために、より多くの情報に行きます

私は、MS ExchangeからZimbraに移行しようとしている組織にいます。移行の一環として、メールボックスを転送する優れたユーティリティが見つかりましたが、PSTファイルをZimbraの使用可能な形式に転送することは困難です。

長い話を短くするためにいくつかの解決策を見つけましたが、1つの例外を除いてpstファイルを使用可能な形式にスピンアウトできました。

そのフォルダにメールを置く代わりに、“mbox”と呼ばれるサブディレクトリが作成されます。おそらく、.emlファイルが分割されるmboxファイルに似ています。各ユーザーは異なり、他のユーザーとは異なるフォルダー構造を使用するため、スクリプトが実行されるまで各ディレクトリは不明です。さらに、mboxフォルダーの内容も一意です。

これらのディレクトリでは、mboxという名前のフォルダのみが定数であるため、findを使用してこれらのフォルダを見つけ、そのフォルダの内容を取得して1つのディレクトリに移動し、mboxという名前のフォルダを削除します。スクリプトに表示される実際のコマンドは次のとおりです。

find localFolders -name mbox -type d -exec mv {}/* .. \;

またはあなたが与えた例では:

find localFolders -name mbox -type d -execdir sh -c 'echo mv {}/* ..' \;

*をワイルドカードとして使用して、mboxフォルダー内のすべてを選択しようとしています。私が知る限り、それは代わりにディレクトリの一部として扱われ、意図したとおりのワイルドカードではありません

考え?

2
Don

これはどう?

find ./localFolders -name 'file' -type d -execdir bash -c 'mv -vnt . -- "$0"/*' {} \; -Prune

ファイル名のスペースと面白い記号に関しては安全です。楽しい!

1
gniourf_gniourf

findを使用して適切に行うには、次の概念を理解する必要があります。

  1. プロセスの作業ディレクトリ。
  2. Bash(または他のシェル)のglob機能。

Linuxのすべてのプロセスには、内部状態の一部として作業ディレクトリがあります。ファイルシステム内のプロセスの「場所」として想像できます。シェルもプロセスであり、作業ディレクトリがあります。シェルの作業ディレクトリは、(通常)プロンプトに表示されるディレクトリです。

プロセスが作成されると、その親から作業ディレクトリを継承します。シェルからプロセスを開始する場合、シェルは親です。すべてのプロセスは、独自の作業ディレクトリを自由に変更できます。組み込みコマンドcdを使用して、シェルの作業ディレクトリを変更できます。

作業ディレクトリは、相対ファイル名を扱うときに使用されます。相対ファイル名は、/で始まらないファイル名です。たとえば、cat fooを実行すると、現在の作業ディレクトリでファイルfooが検索されますが、cat /tmp/fooを実行すると、/tmpでファイルfooが検索されます。

find -execを扱うときは、常に作業ディレクトリについて考える必要があります。 -execの作業ディレクトリは、findの作業ディレクトリです。 findの作業ディレクトリは、シェルの作業ディレクトリです。

あなたの場合:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

..は、コマンドを実行するまでに、シェルの作業ディレクトリであるmvの作業ディレクトリである-execの作業ディレクトリであるfindの作業ディレクトリの親ディレクトリを参照します。それはあなたが望んでいたものではありません。

-execdir-execに似ていますが、最初に作業ディレクトリを、一致するアイテムが見つかったディレクトリに切り替えます。 -execdirは質問の解決に向けた大きな一歩ですが、findコマンドには別の問題があります:* a.k.a. glob。

グロブはワイルドカードのようなものです。たとえば、foo*foobarおよびfoobazに一致します(および*もヌル文字列に一致するため、foo)。ここまでは順調ですね。グロビングの興味深い部分は、実行されたコマンドではなく、シェルによって行われることです。 rm foo*を実行すると、シェルはまず*foobarおよびfoobazに展開し、rm foobar foobazを実行します。 rm*を見ることはありません。

現在のディレクトリにfooで始まるファイルがない場合はどうなりますか?その後、シェルは(通常)*を展開せず、*を逐語的にコマンドに渡します。つまり、rm foo*rm foo*として実行されます。 rmfoo*という名前のファイルを探しますが、(ほとんどの場合)ファイルを見つけられず、エラーで中断します。あなたが疑問に思っている場合:はい、ファイル名に*を含めることは可能です。いいえ、それを自宅で試してはいけません。

あなたの場合:

find ./localFolders -name 'file' -type d -exec mv {}/* .. \;

findコマンドを実行する前に、シェルは最初にグロブ{}/*を展開しようとします。パターン{}/*に一致するファイルはないため、グロブはそのまま渡されます。

findコマンドの機能を見てみましょう。 /home/username/somedirからfindコマンドを実行しており、その中にいくつかのファイルがあるディレクトリ/home/username/somedir/localFolders/foodir/fileが存在するとします。 findはパラメータで指定されているため/home/username/somedir/localFoldersで検索を開始しますが、findの作業ディレクトリはそこから実行されたため/home/username/somedirです。

これでfindがディレクトリ/home/username/somedir/localFolders/foodir/fileを見つけました。 -execを実行する前に、{}を見つかったアイテムに置き換えます。つまり、mv {}/* ..mv /home/username/somedir/localFolders/foodir/file/* ..になります。 mvは、(おそらく)*という名前のファイルを見つけられないため失敗します。

*という名前のファイルがあり、mvがファイルを..に移動するとします。作業ディレクトリは/home/username/somedirであることを忘れないでください。つまり、mvはファイルを/home/username/somedir/..である/home/usernameに移動します。それはあなたが望んでいたものではありません。

findコマンドの「修正」は、いくつかの要因に依存します。例えば:

  • fileディレクトリに「ドットファイル」がありますか?ドットファイルは、ドットで始まる名前のファイルです。 * globは(通常)ドットで始まるファイル名には展開されません。
  • fileディレクトリに他のディレクトリはありますか? findはそれらに再帰しようとしますが、移動されているため失敗します。
  • fileディレクトリにfileという名前のディレクトリがありますか?それらのファイルはどこに行くべきですか?
  • fileディレクトリに膨大な数のファイルがありますか?それは* glob展開を台無しにするかもしれません。
  • fileディレクトリからのファイルとファイル名の衝突はありますか?上書きする必要がありますか?
  • ファイル名にスペースがありますか?それはすべてを台無しにします。

findコマンドを修正するために、これらすべての要因を喜んで無視します。

-execdirおよびsh -cを使用して、*の展開を遅らせる修正方法を次に示します。

find ./localFolders -name file -type d -execdir sh -c 'mv {}/* ..' \;

*は引用符で囲まれているため、事前に展開されません。 -execdirがコマンドを実行すると、sh -cは作業ディレクトリ内の*を展開します。

sh -cを使用しない別の修正を次に示します。

find ./localFolders -path "*/file/*" -execdir mv {} .. \;

-pathを使用して、fileという名前のディレクトリの下にあるすべてのアイテムを見つけました。 *は引用符で囲まれているため、事前に拡張されません。今回はfind*の解釈を処理します。これは./localFolders/foodir/file/bardir/file/somefileのようなものにも一致することに注意してください。言ったように、私は喜んでその要素を無視しました。

完全を期すため、-execdirを使用しない別の修正:

find ./localFolders -name file -type d -exec sh -c 'mv {}/* {}/..' \;
1
lesmana