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

StackOverflow https://stackoverflow.com/questions/143171

質問

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

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

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

これは Mac  OS  X 10.5.3 (Leopard)にBSD xargs

役に立ちましたか?

解決

これらすべてを単一の find コマンドに結合できます:

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

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

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

他のヒント

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

grep -null をサポートしているかどうかも、 xargs -0 をサポートしているかどうかもわかりません、Leopardでも、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 のマニュアルページから、" ...置換に表示されるアンパサンド( ``& '')は、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 {} +

より一般的な; ではなく、末尾の + に注意してください。

このソリューション:

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

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

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

  • は、他のすべての答えとは限らないが、受け入れられているものよりもより効率的速い)です。

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

findの-print0オプションでxargsに--nullコマンドラインオプションを使用する方法を調べます。

find以外のコマンドに依存している人、例えば ls

find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
find | perl -lne 'print quotemeta' | xargs ls -d

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

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

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"間のPerlの行ライナーおよび" xargs"各ブランクをエスケープ/引用符で囲むため、「xargs」空白が埋め込まれたファイル名を&ls;に渡します;単一の引数として。

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

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

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

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

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

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

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

システム上の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でテスト済み

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

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

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

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

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

$ true | xargs echo no data
no data

$ true | xargsL echo no data # No output

xargsLに与えられた引数はすべてxargsに渡されます。

これは&quot; xargsL&quot;です。 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 "<*> failed!" >& 2' 0

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

スクリプトを$ PATHのディレクトリに入れます。忘れないでください

$ chmod + x xargsL

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

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

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

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

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

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

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

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

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

1つには、全体で〜80行のコードがあるため、何が起こっているかを簡単に把握でき、異なる動作が必要な場合は、それよりも短い時間で新しいスクリプトにハッキングできますStack&nbsp; 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で書き直してください。

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

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

フレームチャレンジ&#8212;あなたは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コマンド{} + バリアントは、本質的に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で使用することで、パイプやシェルの呼び出しが不要になり、ファイル名に含まれる厄介な文字を心配する必要がなくなります。

Bashを使用している場合、 mapfile によって stdout を行の配列に変換できます:

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

利点は次のとおりです。

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

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

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

欠点:

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

まあ... OS XでBashが利用可能かどうかは誰が知っていますか?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top