ディレクトリの下に多数のファイルをコピーしようとしていますが、多くのファイルには名前にスペースと一重引用符が含まれています。 find
とgrep
をxargs
でつなげようとすると、次のエラーが表示されます。
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
Xargsをより堅牢に使用するための提案はありますか?
これは Mac OS X 10.5. (Leopard)とBSD xargs
にあります。
これらすべてを単一のfind
コマンドに組み合わせることができます。
find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;
これは、スペースを含むファイル名とディレクトリを処理します。 -name
を使用して、大文字と小文字を区別した結果を取得できます。
注:cp
に渡される--
フラグは、-
で始まるファイルをオプションとして処理できないようにします。
find . -print0 | grep --null 'FooBar' | xargs -0 ...
Leopardではgrep
が--null
をサポートするかどうか、またxargs
が-0
をサポートするかどうかはわかりませんが、GNUではすべて問題ありません。
元のポスターが望んでいることを行う最も簡単な方法は、区切り文字を空白から次のような行末文字に変更することです。
find whatever ... | xargs -d "\n" cp -t /var/tmp
これは、「cp」を複数回実行しないため、より効率的です。
find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
私は同じ問題に遭遇しました。ここに私がそれを解決した方法があります:
find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar
sed
を使用して、入力の各行を同じ行に置き換えましたが、二重引用符で囲みました。 sed
manページから、「...置換に現れるアンパサンド( ``& '')は、RE ... "に一致する文字列に置き換えられます-この場合、.*
、行全体。
これにより、xargs: unterminated quote
エラーが解決されます。
このメソッドは Mac OS X v10.7.5 (ライオン)で動作します:
find . | grep FooBar | xargs -I{} cp {} ~/foo/bar
また、投稿した正確な構文もテストしました。また、10.7.5でも問題なく機能しました。
xargs
を使用しないでください。それはきちんとしたプログラムですが、自明でないケースに直面したとき、find
とうまく行きません。
ポータブル(POSIX)ソリューション、つまりfind
、xargs
またはcp
GNU固有の拡張機能を必要としないソリューションを次に示します。
find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +
通常の+
ではなく、末尾の;
に注意してください。
このソリューション:
スペース、改行、その他の特殊文字が埋め込まれたファイルとディレクトリを正しく処理します。
GNUツールキットを提供していないシステムでも、UnixおよびLinuxシステムで動作します。
素敵で便利なプログラムであるxargs
は使用しませんが、find
の出力を適切に処理するには、調整と非標準機能が多すぎます。
また、より効率的(読み取り高速)が受け入れられており、他のすべての答えではないにしてもほとんどの答えよりも優れています。
また、{}
を引用する他の返信やコメントに記載されていることは役に立たないことにも注意してください(エキゾチックなfish
Shellを使用している場合を除く)。
Findで-print0オプションを指定したxargsの--nullコマンドラインオプションの使用を検討します。
Find以外のコマンドに依存する人、例えばls
:
find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
次の構文が私にとってうまく機能することがわかりました。
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」に渡します。
find | Perl -lne 'print quotemeta' | xargs ls -d
これは、改行以外のすべての文字に対して確実に機能すると思います(そして、ファイル名に改行がある場合、これよりも悪い問題があると思います)。それはGNU findutilsを必要とせず、Perlだけを必要とするので、どこでもかなり動作するはずです。
私にとっては、少し違うことをしようとしていました。 .txtファイルをtmpフォルダーにコピーしたかった。 .txtファイル名にはスペースとアポストロフィ文字が含まれています。これは私のMacで機能しました。
$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/' | xargs -I{} cp -v {} ./tmp/
Bash(POSIXではない)を使用すると、プロセス置換を使用して、変数内の現在の行を取得できます。これにより、引用符を使用して特殊文字をエスケープできます。
while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
他の回答で説明したオプションのほとんどは、GNUユーティリティを使用しないプラットフォーム(Solaris、AIX、HP-UXなど)では標準ではないことに注意してください。 「標準」xargsの動作については、 POSIX の仕様を参照してください。
また、xargsの動作により、コマンドが少なくとも1回実行され、入力がなくても迷惑になることがわかります。
名前内のスペースの問題に対処するために、xargs(xargl)のプライベートバージョンを作成しました(改行のみが分離されます-ただし、 'find ... -print0'と 'xargs -0'の組み合わせは、ファイル名がASCII NUL '\ 0'文字を含むxarglは、特にGNUには少なくとも同等の機能があるため、公開する価値があるために必要なほど完全ではありません。
ほとんどの問題に対処する「xargs」を囲む「xargsL」という小さなポータブルラッパースクリプトを作成しました。
Xargsとは異なり、xargsLは行ごとに1つのパス名を受け入れます。パス名には、(明らかに)改行またはNULバイトを除く任意の文字を含めることができます。
ファイルリストでは引用符の使用は許可またはサポートされていません-ファイル名にはあらゆる種類の空白、バックスラッシュ、バックティック、シェルワイルドカード文字などを含めることができます-xargsLはそれらをリテラル文字として処理しますが、害はありません。
追加のボーナス機能として、xargsLはnot入力がない場合にコマンドを1回実行します!
違いに注意してください:
$ true | xargs echo no data
no data
$ true | xargsL echo no data # No output
XargsLに与えられた引数はすべてxargsに渡されます。
「xargsL」POSIX Shellスクリプトは次のとおりです。
#! /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
スクリプトを実行可能にします。
Solarisで Bill Starの回答 を少し変更しました:
find . -mtime +2 | Perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file
これにより、各行が引用符で囲まれます。 「-l」オプションは使用しませんでしたが、おそらく役立つでしょう。
私が行っていたファイルリストには「-」が含まれている場合がありますが、改行は含まれていません。 xargsを使用して大量の削除を開始する前に、見つかった内容を確認したいので、他のコマンドで出力ファイルを使用していません。
私はこれを少し試して、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で書き直してください。
bill_starrのPerlバージョン は、埋め込まれた改行ではうまく機能しません(スペースのみを処理します)。たとえばGNUツールがないSolarisでは、より完全なバージョンが(sedを使用して)あるかもしれません...
find -type f | sed 's/./\\&/g' | xargs grep string_to_find
findおよびgrep引数または他のコマンドを必要に応じて調整しますが、sedは埋め込まれた改行/スペース/タブを修正します。
システム上の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でテスト済み
フレームチャレンジ— 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で使用することにより、パイプまたはシェルの呼び出しが不要になり、ファイル名に含まれる厄介な文字を心配する必要がなくなります。
次のようなFoobarディレクトリのgrepが必要になる場合があります。
find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .