web-dev-qa-db-ja.com

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

ディレクトリの下に多数のファイルをコピーしようとしていますが、多くのファイルには名前にスペースと一重引用符が含まれています。 findgrepxargsでつなげようとすると、次のエラーが表示されます。

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

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

これは Mac OS X 10.5. (Leopard)とBSD xargsにあります。

216
Drew Stephens

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

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

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

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

191
godbyk

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

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

114

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

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

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

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

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

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

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

これにより、xargs: unterminated quoteエラーが解決されます。

55
oyouareatubeo

このメソッドは Mac OS X v10.7.5 (ライオン)で動作します:

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

また、投稿した正確な構文もテストしました。また、10.7.5でも問題なく機能しました。

50
the_minted

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

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

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

通常の+ではなく、末尾の;に注意してください。

このソリューション:

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

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

  • 素敵で便利なプログラムであるxargsは使用しませんが、findの出力を適切に処理するには、調整と非標準機能が多すぎます。

  • また、より効率的(読み取り高速)が受け入れられており、他のすべての答えではないにしてもほとんどの答えよりも優れています。

また、{}を引用する他の返信やコメントに記載されていることは役に立たないことにも注意してください(エキゾチックなfishShellを使用している場合を除く)。

11
jlliagre

Findで-print0オプションを指定したxargsの--nullコマンドラインオプションの使用を検討します。

8
Shannon Nelson

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

5
bill_starr
find | Perl -lne 'print quotemeta' | xargs ls -d

これは、改行以外のすべての文字に対して確実に機能すると思います(そして、ファイル名に改行がある場合、これよりも悪い問題があると思います)。それはGNU findutilsを必要とせず、Perlだけを必要とするので、どこでもかなり動作するはずです。

5
mavit

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

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

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

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

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

また、xargsの動作により、コマンドが少なくとも1回実行され、入力がなくても迷惑になることがわかります。

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

2

ほとんどの問題に対処する「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

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

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

1
John Allsup

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

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

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

1
rjb1

システム上の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
Jan Ptáčník

フレームチャレンジ— 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で使用することにより、パイプまたはシェルの呼び出しが不要になり、ファイル名に含まれる厄介な文字を心配する必要がなくなります。

0
gerrit

次のようなFoobarディレクトリのgrepが必要になる場合があります。

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