xargsを使用して、名前にスペースと引用符が含まれているファイルをコピーするにはどうすればよいですか?


232

ディレクトリの下に一連のファイルをコピーしようとしていますが、いくつかのファイルの名前にはスペースと単一引用符が含まれています。私は一緒に文字列にしようとするfindgrepしてxargs、私は次のエラーを取得します:

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

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

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


2
一重引用符を含むファイル名を使用したこの場合のGNU xargsエラーメッセージは、「xargs:一致しない一重引用符です。デフォルトでは、-0オプションを使用しない限り、引用符はxargsにとって特別なものです」という方が便利です。
スティーブジェソップ

3
GNU xargsには--delimiterオプション(-d)もあります。\n区切り文字として使用してみてください。これによりxargs、スペースのある行が複数の単語/引数に分割されるのを防ぎます。
MattBianco 2017

回答:


199

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

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

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

注:に--渡されたフラグは、オプションでcp始まるファイルを処理しないようにし-ます。


70
実行可能ファイルを毎回200の引数で5回呼び出す方が、毎回1つの引数で1000回呼び出すよりも速いため、xargsを使用します。
tzot

12
Chris Jester-Youngからの答えは「良い答え」になるはずです...ところで、この解決策は、ファイル名が「-」で始まる場合は機能しません。少なくとも、cpの後には「-」が必要です。
ケルティア2009年

11
速度の例-829ファイルを超える場合、「find -exec」メソッドは26秒かかりましたが、「find -print0 | xargs --null」メソッドツールは0.7秒でした。有意差。
Peter Porter

7
@tzot遅いコメントですが、とにかく、xargsあなたが説明している問題に対処する必要はありません。findすでに-exec +句読点でサポートされています。
jlliagre 2013年

3
はスペースの扱い方の質問に答えません
ベングラッサー

117

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

私はどうか知らないgrepサポート--null、またかどうかをxargsサポートし-0、Leopard上ではなく、GNUにそれはすべての良いことです。


1
Leopardは「-Z」(GNU grepです)をサポートしています。もちろん、find(1)とxargs(1)は「-0」をサポートしています。
Keltia、2009年

1
OS X 10.9 grep -{z|Z}では、「zgrepとして動作する(解凍)」という意味であり、「各ファイル名の後に0バイトを出力する」という意味ではありません。grep --null後者を達成するために使用します。
bassim 14

4
何が問題になっていfind . -name 'FooBar' -print0 | xargs -0 ...ますか?
クエンティンプラデット2014年

1
@QuentinPradet明らかに、 "FooBar"のような固定文字列の場合、-nameまたは問題なく-path動作します。OPはの使用を指定しています。これはgrep、おそらく正規表現を使用してリストをフィルタリングしたいためです。
Chris Jester-Young

1
だのHi-エンジェル@ まさに私が使用する理由xargs -0 と一緒に find -print0。後者はNULターミネーターでファイル名を出力し、前者はその方法でファイルを受け取ります。どうして?Unixのファイル名には、改行文字を含めることができます。ただし、NUL文字を含めることはできません。
Chris Jester-Young

92

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

find whatever ... | xargs -d "\n" cp -t /var/tmp

4
このanwserは単純で効果的で、要点はまっすぐです。xargsのデフォルトの区切り文字セットは広すぎるため、OPが実行したいことのために狭める必要があります。今日、cygwinを除いて、まったく同じ問題に遭遇し、同じようなことをしているので、私はこれを直接知っています。xargsコマンドのヘルプを読んだ場合、いくつかの頭痛の種を回避できたかもしれませんが、あなたの解決策で修正されました。よろしくお願いします!(そうです、OPはBSD xargsを使用するMacOS上にありましたが、私は使用していませんが、xargs "-d"パラメータがすべてのバージョンに存在することを期待しています)。
Etienne Delavennat 2016年

7
いい答えですが、Macでは動作しません。代わりに、検索を パイプしsed -e 's_\(.*\)_"\1"_g'てファイル名を引用符で囲むことができます
ishahak

10
これは受け入れられる答えになるはずです。問題はの使用についてでしたxargs
Mohammad Alhashash 2016年

2
私が得るxargs: illegal option -- d
ネヘム

1
多くの* nixシステムでは、ファイル名に改行文字が含まれる可能性があることに注意してください。実際にこれに遭遇することはほとんどありませんが、信頼できない入力でシェルコマンドを実行している場合、これは問題になる可能性があります。
Soren Bjornstad

71

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

find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar

1
これは私にはうまくいきませんでした。〜/ foo / barをあなたが見つけたものにcpしようとしましたが、反対はしませんでした
Shervin Asgari

13
cpの-tフラグは、GNU拡張であるAFAIKであり、OS Xでは使用できません。しかし、もしそうであれば、この回答に示されているように機能します。
metamatt 2012年

2
Linuxを使用しています。「-t」スイッチをお寄せいただきありがとうございます。それは私が欠けていたものです:-)
Vahid Pazirandeh

59

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

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

私が使用しsed、同じラインで入力のそれぞれの行を置換するが、二重引用符で囲まれています。sedmanページから、「...置換に現れるアンパサンド(「&」)はRE ...に一致する文字列に置き換えられます。この場合は.*、行全体です。

これはxargs: unterminated quoteエラーを解決します。


3
私はWindowsを使用sed s/.*/\"&\"/していて、gnuwin32を使用しているので、それを機能させるために使用する必要がありました。
パット

はい、しかしおそらくこれは"inでファイル名を処理しません-sedも引用符で囲まない限り?
artfulrobot

使用sedは天才であり、今のところ問題を書き直すことなく正しい解決策です!
entonio 2015年

53

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

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

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


4
これは機能しますが、-I意味がある-L 1ので(マニュアルにもそうです)、つまり、cpコマンドがファイルごとに1回実行される= v遅いです。
artfulrobot 2015年

xargs -J%cp%<destination dir> OSXではおそらくより効率的です。
ウォーカーD

3
申し訳ありませんが、これは間違っています。まず、TOが回避したかったエラーを正確に生成します。find ... -print0xargs -0を使用して、xargsの「デフォルトでは引用符は特別です」という問題を回避する必要があります。次に、スペースや特殊文字から保護するために、xargsに渡されるコマンドでは通常'{}'not {}を使用します。
Andreas Spindler 2016年

3
Andreas Spindlerさん、申し訳ありません。xargsにはあまり詳しくないため、この行を実験してみました。コメントし、賛成票を投じたほとんどの人にとってうまくいくようです。どのようなエラーが発生するかについてもう少し詳しく説明してもらえますか?また、あなたがより正確だと思う正確な入力を投稿していただけませんか?ありがとうございました。
the_minted 2016年

12

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

ポータブル(POSIX)ソリューション、つまりを必要としないソリューションfindxargsまたはcpGNU固有の拡張機能を次に示します。

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

+通常の代わりにエンディングに注意してください;

このソリューション:

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

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

  • xargswhichは使用しません。これは便利で便利なプログラムですが、find出力を適切に処理するには、微調整や非標準の機能が必要です。

  • また、ある方が効率的(読み速く受け入れられていない他のすべての答えの場合はほとんどより)。

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



1
@PeterMortensenあなたはおそらくエンディングプラスを見逃しています。オーバーヘッドなしでfindxargsができるか。
jlliagre



6
find | perl -lne 'print quotemeta' | xargs ls -d

これはラインフィード以外のすべての文字で確実に機能すると思います(ファイル名にラインフィードがある場合、これよりも問題が深刻になると思います)。GNU findutilsを必要とせず、Perlのみを必要とするため、どこでもかなり機能するはずです。


ファイル名に改行を含めることはできますか?聞いたことがない。
mtk

2
確かにそうです。たとえば、試してくださいmkdir test && cd test && perl -e 'open $fh, ">", "this-file-contains-a-\n-here"' && ls | od -tx1
mavit

1
|perl -lne 'print quotemeta'まさに私が探していたものです。PHPファイルの数を大幅に減らしてマルウェアに感染したファイルにまで減らすfind必要があったため、ここの他の投稿は役に立ちませんgrep -rlでした。
Marcos

perlとquotemetaはprint0 / -0よりもはるかに一般的です
bmike

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"に渡します。


3

フレームチャレンジ— xargsの使用方法を尋ねています。答えは、xargsは必要ないため、使用しないことです。

コメントにはuser80168すべてのファイルのためのCPを呼び出さず、CPと直接これを行う方法について説明します。

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

これは機能します:

  • cp -tフラグはの先頭近くターゲットディレクトリを与えることを可能にするcpのではなく、終わり近く。からman cp
   -t, --target-directory=DIRECTORY
         copy all SOURCE arguments into DIRECTORY
  • --フラグが伝えcpファイルが始まるので、ファイル名ではなく、フラグとして後にすべてのものを解釈する-か、--混同しないでくださいcp-/ --文字はによって解釈されるので、これはまだ必要ですがcp、他の特殊文字はシェルによって解釈されます。

  • find -exec command {} +バリアントは、基本的にxargsのと同じこと。からman find

   -exec command {} +                                                     
         This  variant  of the -exec action runs the specified command on
         the selected files, but the command line is built  by  appending
         each  selected file name at the end; the total number of invoca‐
         matched  files.   The command line is built in much the same way
         that xargs builds its command lines.  Only one instance of  `{}'
         is  allowed  within the command, and (when find is being invoked
         from a shell) it should be quoted (for example, '{}') to protect
         it  from  interpretation  by shells.  The command is executed in
         the starting directory.  If any invocation  returns  a  non-zero
         value  as exit status, then find returns a non-zero exit status.
         If find encounters an error, this can sometimes cause an immedi‐
         ate  exit, so some pending commands may not be run at all.  This
         variant of -exec always returns true.

これをfindで直接使用することにより、パイプラインやシェルの呼び出しが不要になり、ファイル名に含まれる厄介な文字を気にする必要がなくなります。


驚くべき発見、私はまったくわかりませんでした!!! "-exec utility [argument ...] {} + -execと同じですが、utilityを呼び出すたびに、「{}」ができるだけ多くのパス名に置き換えられます。この動作は、xargs(1 )」BSD実装では。
2019年

2

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

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

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


2
GitHubまたはそれは起こりませんでした
Corey Goldberg

@CoreyGoldberg:その時は起こらなかったと思います。
ジョナサンレフラー

POSIX findはそもそも必要ありませんxargs(それは11年前にはすでに当てはまりました)。
jlliagre

2

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

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)

2

私にとっては、少し違うことをしようとしていました。.txtファイルをtmpフォルダーにコピーしたいと思いました。.txtファイル名には、スペースとアポストロフィ文字が含まれています。これは私のMacで動作しました。

$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/'  | xargs -I{} cp -v {} ./tmp/

1

システムのfindおよびxargバージョンがサポート-print0および-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でテスト済み


1

ほとんどの問題に対処する「xargs」の周りに「xargsL」という小さなポータブルラッパースクリプトを作成しました。

xargsとは異なり、xargsLは1行に1つのパス名を受け入れます。パス名には、(明らかに)改行またはNULバイト以外の任意の文字を含めることができます。

ファイルリストでの引用は許可またはサポートされていません。ファイル名には、あらゆる種類の空白、バックスラッシュ、バックティック、シェルワイルドカード文字などが含まれている可能性があります。xargsLはそれらをリテラル文字として処理します。害はありません。

追加のボーナス機能として、入力がない場合、xargsLはコマンドを1回実行しません

違いに注意してください:

$ true | xargs echo no data
no data

$ true | xargsL echo no data # No output

xargsLに指定された引数は、xargsに渡されます。

以下は、 "xargsL" POSIXシェルスクリプトです。

#! /bin/sh
# Line-based version of "xargs" (one pathname per line which may contain any
# amount of whitespace except for newlines) with the added bonus feature that
# it will not execute the command if the input file is empty.
#
# Version 2018.76.3
#
# Copyright (c) 2018 Guenther Brunthaler. All rights reserved.
#
# This script is free software.
# Distribution is permitted under the terms of the GPLv3.

set -e
trap 'test $? = 0 || echo "$0 failed!" >& 2' 0

if IFS= read -r first
then
        {
                printf '%s\n' "$first"
                cat
        } | sed 's/./\\&/g' | xargs ${1+"$@"}
fi

スクリプトを$ PATHのディレクトリに配置し、忘れずに

$ chmod +x xargsL

そこにスクリプトを実行可能にします。


1

bill_starrのPerlバージョンは、埋め込まれた改行に対しては適切に機能しません(スペースのみに対応しています)。たとえば、GNUツールを使用していないSolarisなどの場合、より完全なバージョンは(sedを使用して)なる可能性があります...

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

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


1

Solarisで少し変更したBill Starの回答を使用しました。

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

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

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


1

私はこれを少し試して、xargsの変更を検討し始めました。ここで話しているようなユースケースでは、Pythonでの単純な再実装の方が優れていることに気付きました。

1つには、全体で約80行のコードがあるため、何が起こっているのかを簡単に把握でき、異なる動作が必要な場合は、取得するよりも短い時間で新しいスクリプトにハッキングできます。 Stack Overflowのような場所での返信。

見る 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で書き直してください。


0

次のようにFoobarディレクトリをgrepする必要があるかもしれません。

find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .

1
マニュアルページによる-iと、非推奨であり、-I代わりに使用する必要があります。
Acumenus 2014

-1

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

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

利点は次のとおりです。

  • それは組み込みなので、より高速です。
  • すべてのファイル名を指定してコマンドを一度に実行すると、処理が速くなります。
  • ファイル名に他の引数を追加できます。以下のためcpにもすることができます:

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

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

欠点:

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.