242

ディレクトリの下に一連のファイルをコピーしようとしていますが、多くのファイルの名前にスペースと単一引用符が含まれています。findとをgrep一緒に文字列にしようとするとxargs、次のエラーが発生します。

find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote

xargs をより堅牢に使用するための提案はありますか?

これは、BSD を使用したMac OS X 10.5.3 (Leopard)上にありxargsます。

4

22 に答える 22

206

findこれらすべてを 1 つのコマンドに組み合わせることができます。

find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;

これにより、スペースを含むファイル名とディレクトリが処理されます。を使用-nameして、大文字と小文字を区別する結果を得ることができます。

注:--に渡されたフラグにより​​、 as オプションcpで始まるファイルを処理できなくなります。-

于 2008-09-27T08:22:19.750 に答える
122

find . -print0 | grep --null 'FooBar' | xargs -0 ...

が Leopard で をサポートしているかどうかも、 をサポートしているかどうかもわかりませんが、GNU では問題grepあり--nullません。xargs-0

于 2008-09-27T07:15:54.557 に答える
101

元の投稿者が望んでいることを行う最も簡単な方法は、次のように区切り文字を空白から行末文字に変更することです。

find whatever ... | xargs -d "\n" cp -t /var/tmp
于 2015-11-04T17:21:16.440 に答える
74

「cp」を複数回実行しないため、これはより効率的です。

find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
于 2008-09-29T14:50:21.390 に答える
65

私は同じ問題に遭遇しました。これが私がそれを解決した方法です:

find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar

以前sedは、入力の各行を同じ行に置き換えていましたが、二重引用符で囲んでいました。sedマニュアルページから、「 ...置換に表示されるアンパサンド( ``&'')は、REに一致する文字列に置き換えられます... "-この場合、.*行全体。

これでエラーが解決しxargs: unterminated quoteます。

于 2012-08-22T05:57:43.863 に答える
56

この方法は、Mac OS X v10.7.5 (Lion) で機能します。

find . | grep FooBar | xargs -I{} cp {} ~/foo/bar

あなたが投稿した正確な構文もテストしました。10.7.5 でも問題なく動作しました。

于 2013-08-28T19:20:38.767 に答える
13

使用しないでくださいxargsfindこれはきちんとしたプログラムですが、重要なケースに直面するとうまくいきません。

以下は移植可能な (POSIX) ソリューション、つまりやGNU 固有の拡張機能を必要findとしないソリューションです。xargscp

find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +

+より一般的な ではなく、エンディングに注意してください;

このソリューション:

  • スペース、改行、その他のエキゾチックな文字が埋め込まれたファイルとディレクトリを正しく処理します。

  • GNU ツールキットを提供していないものも含め、任意の Unix および Linux システムで動作します。

  • これはxargs素晴らしく便利なプログラムですが、出力を適切に処理するには微調整や非標準機能が多すぎますfind

  • また、他のすべてではないにしても、受け入れられているほとんどの回答よりも効率的です(より速く読む)。

また、他の返信やコメントに記載されていることにもかかわらず、引用{}は役に立たないことに注意してください(エキゾチックfishシェルを使用していない限り)。

于 2013-06-26T16:41:06.867 に答える
8

find 以外のコマンドに依存している場合、たとえばls次のようになります。

find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
于 2014-03-20T14:10:43.927 に答える
8

find で -print0 オプションを指定して xargs の --null コマンドライン オプションを使用することを検討してください。

于 2008-09-27T07:16:20.203 に答える
6
find | perl -lne 'print quotemeta' | xargs ls -d

これは、改行を除くすべての文字に対して確実に機能すると思います (ファイル名に改行が含まれている場合は、これよりもさらに悪い問題が発生するのではないかと思います)。GNU findutils を必要とせず、Perl のみを必要とするため、ほとんどどこでも動作するはずです。

于 2012-02-09T22:23:51.527 に答える
5

次の構文がうまく機能することがわかりました。

find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200

この例では、「/usr/pcapps」にマウントされたファイルシステムで、1,000,000 バイトを超える最大の 200 個のファイルを探しています。

「find」と「xargs」の間の Perl ラインライナーは、各空白をエスケープ/引用するため、「xargs」は空白が埋め込まれたファイル名を単一の引数として「ls」に渡します。

于 2009-01-23T22:39:33.383 に答える
3

Bash (POSIX ではない) では、プロセス置換を使用して、変数内の現在の行を取得できます。これにより、引用符を使用して特殊文字をエスケープできます。

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
于 2014-01-01T13:09:45.457 に答える
2

他の回答で説明されているオプションのほとんどは、GNUユーティリティを使用しないプラットフォーム(Solaris、AIX、HP-UXなど)では標準ではないことに注意してください。'標準の'xargsの動作については、 POSIX仕様を参照してください。

また、入力がなくてもコマンドを少なくとも1回実行するxargsの動作は、厄介なものであることがわかりました。

名前のスペースの問題に対処するために、独自のプライベートバージョンのxargs(xargl)を作成しました(ただし、ファイル名ができないことを考えると、「find...-print0」と「xargs-0」の組み合わせはかなりきれいです。 ASCII NUL'\ 0'文字が含まれています。私のxarglは、公開する価値があるほど完全ではありません。特に、GNUには少なくとも同じくらい優れた機能があるためです。

于 2008-10-02T07:00:01.090 に答える
1

私はこれで少し遊んで、xargs の変更を検討し始めました。そして、ここで話している種類のユースケースでは、Python での単純な再実装がより良いアイデアであることに気付きました。

1 つには、全体で約 80 行のコードがあるということは、何が起こっているのかを簡単に把握できることを意味し、別の動作が必要な場合は、取得するよりも短い時間でそれを新しいスクリプトにハックすることができます。スタックオーバーフローのような場所での返信。

https://github.com/johnallsup/jda-misc-scripts/blob/master/yargsおよびhttps://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.pyを参照してください。

yargs が記述されている (そして Python 3 がインストールされている) 場合、次のように入力できます。

find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar

一度に203個のファイルをコピーします。(もちろん、ここで 203 は単なるプレースホルダーであり、203 のような奇妙な数を使用すると、この数に他の意味がないことが明らかになります。)

本当に高速で Python を必要としないものが必要な場合は、zargs と yargs をプロトタイプとして、C++ または C で書き直してください。

于 2015-08-05T19:54:41.323 に答える
1

Solarisでわずかに変更されたBill Starの回答を使用しました:

find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file

これにより、各行が引用符で囲まれます。おそらく役立つでしょうが、「-l」オプションは使用しませんでした。

私が行っていたファイルリストには「-」が含まれている可能性がありますが、改行は含まれていません。出力ファイルを他のコマンドで使用していないのは、xargs を介してそれらを大量に削除する前に、見つかったものを確認したいからです。

于 2010-10-31T17:15:31.670 に答える
1

システムの find と xarg のバージョンが-print0and-0スイッチ (たとえば、AIX の find と xargs) をサポートしていない場合は、次の見栄えの悪いコードを使用できます。

 find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest

ここで、sed は xargs のスペースと引用符をエスケープします。

AIX 5.3 でテスト済み

于 2015-03-27T14:49:21.203 に答える
1

bill_starr の Perl バージョンは、埋め込まれた改行に対してうまく機能しません (スペースのみを処理します)。たとえば、GNUツールを持っていないSolarisの場合、より完全なバージョンは(sedを使用して)...

find -type f | sed 's/./\\&/g' | xargs grep string_to_find

必要に応じてfindおよびgrep引数またはその他のコマンドを調整しますが、sedは埋め込まれた改行/スペース/タブを修正します.

于 2009-06-17T01:53:47.073 に答える
-1

Bash を使用している場合は、次の方法でstdoutを行の配列に変換できmapfileます。

find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)

利点は次のとおりです。

  • 組み込みなので高速です。
  • 一度にすべてのファイル名でコマンドを実行すると、高速になります。
  • ファイル名に他の引数を追加できます。cpでは、次のこともできます。

    find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
    

    ただし、一部のコマンドにはそのような機能がありません。

短所:

  • ファイル名が多すぎると、うまくスケーリングできない可能性があります。(制限?わかりませんが、Debianで10000以上のファイル名を含む10 MBのリストファイルで問題なくテストしました)

ええと... OS XでBashが利用できるかどうか誰が知っていますか?

于 2014-05-21T02:50:59.663 に答える