ディレクトリをキャッチするパターンに一致するファイルを移動する

ディレクトリをキャッチするパターンに一致するファイルを移動する

考慮する:

$ ls
foo  xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex

私は find ではなく mv のみを使用して同じ命令シーケンスを実現しようとしています:

$ ls
foo  xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex

さらに(編集)、

$ ls -F
foo/  xyfooz.tex*
$ mv -v *foo* foo

mv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'

「stat」警告が表示されないようにするにはどうすればよいでしょうか。言い換えると、ソースをディレクトリではなくファイルに制限するようにパターンを改良すればよいのではないでしょうか。

GNU bash、4.3.48(1) リリース (x86_64-pc-linux-gnu)

$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"

答え1

mv

cannot move 'foo' to a subdirectory of itselfこの場合は無害なので、無視できます。mv警告にもかかわらず、残りは移動します。ただし、終了ステータスは ではないため、0成功しなかった場合に中止したり修正アクションを実行したりする必要があるスクリプトでは、これが大きな障害となる可能性がありますmv

これはすでにコメントで述べた: mvstderr を でリダイレクトすることで2>/dev/null、警告やエラーを出力しないようにすることができます。ただし、他の警告やエラーもリダイレクトされます。

「パターンを洗練させて、ソースをディレクトリではなくファイルに限定する」のは簡単ではないと思います。それでも、リテラルと一致しないアプローチを取ることはできますfoo。次のアプローチは、ファイルやディレクトリとは関係ありません。名前だけと関係があります。これは、glob が名前を処理するためです。そのため、場合によっては十分ではない可能性があります。

グロブ*foo*は、次の 4 種類のオブジェクトに一致します。

  1. foo自体
  2. プレフィックスのみのfoo、例えばbar-foo– のように一致させることができます*?foo
  3. 接頭辞と接尾辞が付いた foo の例bar-foo-baz*?foo?*
  4. 接尾辞のみの foo は、次のようになりますfoo-bazfoo?*

除外するには、foo後者の 3 つ (2 ~ 4) のみが必要なので、最初のアプローチは次のようになります。

mv *?foo *?foo?* foo?* foo/

シェル オプションが設定されていない限りnullglob、パターンは何かに一致する必要があります。一致しない場合は、文字どおりに渡されますmv。これは望ましくありません。解決策は、事前に次のコマンドを実行することです。

shopt -s nullglob

このシェル オプションは、一致しない glob を何も展開しないようにします。dotglobオプションも調べることをお勧めします。

*?foo*は (2-3) に一致することに注意してください。同様に*foo?*(3-4) にも一致します。さらに、後続の引数はすべてオプションではないことを--伝えることを検討してくださいmv。この方法では、 のようなファイルは--foo(不明な) オプションとして解釈されません。これにより、次の 2 つの優れたコマンドが生まれます。

mv -- *?foo* foo?* foo/

または

mv -- *?foo *foo?* foo/

どちらを選択しても問題ありません。 を使用しないでください。は (例)を2 回 (つまり、2 つの別々の引数として)mv *?foo* *foo?* foo/渡すため、ツールがこのオブジェクトを 2 回目に移動しようとしたときにエラーが発生します。bar-foo-bazmv

一致するものがまったく見つからない場合、私のmvコマンドは退化しmv foo/、エラーをスローします。元のmvコマンド ( nullglobunset を使用) はリテラルを取得し*foo*、別のエラーをスローします。


コンソールセッションの例:

$ shopt -s nullglob
$ mkdir foo
$ touch bar-foo bar-foo-baz foo-baz xyfooz.tex ./--foo
$ mv -v -- *?foo* foo?* foo/
'bar-foo' -> 'foo/bar-foo'
'bar-foo-baz' -> 'foo/bar-foo-baz'
'--foo' -> 'foo/--foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'foo-baz' -> 'foo/foo-baz'
$ 

シンプルなハック

dummy存在しないとしましょう。

mv foo dummy
mv *foo* dummy/
mv dummy foo

ディレクトリ名の変更は、inode ベースのファイルシステムでは非常に高速です。 すでにfoo/開いているファイルを持つプログラムは、inode が重要なので、干渉したり中断したりすることはありません。 ただし、foo/最初の と 3 番目の の間にあるファイル ( を含むパスで)を開く必要があるプログラムがある場合mv、そのプログラムは失敗します。 その他の副作用が発生する場合があります (foo/その間にサードパーティのプログラムが再作成することを想像してください)。これが、この方法をハックと呼ぶ理由です。 使用する前によく考えてください。


とにかくfind

ファイルとディレクトリを区別するのは の仕事ですfind。 で行ったことはfind基本的に正しい方法です。 とmvシェル グロブで行いたいこと (および私が上で行ったこと) は、まあ、せいぜい「あまり正しくない」ようです。これがコマンドです:

find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;

これは、一致したファイルごとにfind別々に を実行するmvため、コマンド全体のパフォーマンスは低下します。このため、単一の に切り替える必要があるかもしれませんmv。これがあなたの考え方である場合 (そして、 と がmv十分findに充実している場合)、次の「非常に正しい」方法を検討する必要があります。

find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +

findコマンドや glob を使用したプレーンな形式に対する利点mv:

  • 1 つのファイルmvで複数のファイルを操作できます。
  • ただし、1 つのコマンド ラインで処理するにはファイルが多すぎる場合は、追加のプロセスfindが実行されます。mv
  • パターンに一致するファイルがない場合、mvまったく実行されません。
  • --のようなファイルのおかげで、--fooは (不明な) オプションとして解釈されませんmv

関連情報