findで見つかったファイルを引数として渡す方法は?


9

まず、些細なしかし不適当答え遮断する:私はどちらも使用することができますfind+のxargsトリックも(のようなその変種findとを-exec私は呼び出しごとにいくつかのような表現を使用する必要があるため)。最後にこれに戻ります。


より良い例として、次のことを考えてみましょう。

$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

それらを引数としてどのように渡すのprogramですか?

それをするだけではうまくいきません

$ ./program $(find -L some/dir -name \*.abc | sort)

program次の引数を取得するため失敗します:

[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc

ご覧のとおり、スペースのあるパスは分割さprogramれており、2つの異なる引数と見なされます。

機能するまで引用する

私のような初心者ユーザーは、そのような問題に直面したときに、最終的に機能するまでランダムに引用符を追加する傾向があります-ここではそれだけでは役に立たないようです…

"$(…)"

$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

引用符は単語分割を防ぐため、すべてのファイルは単一の引数として渡されます。

個々のパスを引用する

有望なアプローチ:

$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"

引用符は確かにあります。しかし、それらはもはや解釈されません。それらは文字列の一部にすぎません。だから彼らは単語分割を妨げなかっただけでなく、彼らは議論に入った!

IFSを変更する

次に、で遊んでみましたIFS。私はとにかくそしてとにかく-彼らが「ワイヤードパス」自体に問題がないように- と一緒にしたいfindと思います。それでは、文字の単語分割を強制して、それをすべて持ってみませんか?-print0sort-znull

$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc

したがって、それでもスペースで分割され、では分割されませんnull

(上記のように)と前のIFS両方に割り当てを配置しようとしました。また、私は次のように他の構文を試してみました、、両方で引用され及び場合とない場合と同様。それらのどれも違いを生むように見えなかった…$(…)./program\0\x0\x00'"$


そして、ここで私はアイデアがありません。私はいくつかのことを試してみましたが、すべてがリストされているのと同じ問題に陥ったようです。

他に何ができますか?それはまったく可能ですか?

もちろん、programパターンを受け入れて検索自体を行うことができました。しかし、それを特定の構文に修正する間は、多くの二重の作業です。(grepたとえば、ファイルを提供するのはどうですか?)

またprogram、パスのリストを含むファイルを受け入れるようにすることもできます。次に、find式を一時ファイルに簡単にダンプして、そのファイルへのパスのみを指定できます。これは直接パスに沿ってサポートされるため、ユーザーが単純なパスだけを持っている場合は、中間ファイルなしで提供できます。しかし、これは良くないように見えます-必要な追加の実装は言うまでもなく、追加のファイルを作成してそれらを処理する必要があります。(ただし、引数としてのファイルの数がコマンドラインの長さに問題を引き起こし始める場合には、これが救いになるかもしれません…)


最後に、find+ xargs(および同様の)トリックは私の場合は機能しないことをもう一度思い出させてください。説明を簡単にするために、私は1つの引数のみを示しています。しかし、私の本当のケースは次のようになります。

$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES

そのためxargs、1つの検索からを実行しても、他の検索に対処する方法が残ります…

回答:


13

配列を使用します。

ファイル名の改行の可能性を処理する必要がない場合は、次のようにすることができます

mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)

その後

./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"

あなたがいる場合行うファイル名内の改行を処理し、bashの> = 4.4を持つ必要性を、あなたが使用することができる-print0-d ''し、アレイ構築中に名をNULLで終了します。

mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)

(および同様にXYZ_FILES)。新しいbash がない場合は、nullで終了する読み取りループを使用して、配列にファイル名を追加できます。

ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)

優秀な!配列について考えていました。しかし、どういうわけか私はそれに何も見つかりませんでしたmapfile(またはその同義語readarray)。しかし、それはうまくいきます!
Adam Badura 2016年

それでも、少し改善することができます。whileループのあるBash <4.4バージョン(たまたま...)では、配列がクリアされません。つまり、ファイルが見つからない場合、配列は未定義です。一方、すでに定義されている場合は、新しいファイルが追加されます(古いファイルを置き換えるのではなく)。declare -a ABC_FILES='()';before を追加するwhileことがトリックのようです。(追加するだけでABC_FILES='()';は機能しません。)
Adam Badura

また、何をない< <、ここで意味ですか?それは同じ<<ですか?これを変更して<<構文エラーが発生するようには思いません( "予期しないトークン`( '")。それで、それは何で、どのように機能しますか?
Adam Badura

(私の特定の使用法に沿った)もう1つの改善点は、さらに別の配列を作成することです。だから私たちはそれらを持っていABC_FILESます。それは結構です。ただしABS_ARGSABC_FILESが空の場合は空の配列、そうでない場合は配列にすることも役立ち('--abc-files' "${ABC_FILES[@]}")ます。このようにして、後でこのように使用できます./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"。空のグループ(存在する場合)に関係なく、正しく機能することを確認してください。または別の言い方をする--abc-filesと、この方法(および--xyz-files)は、実際のパスが後に続く場合にのみ提供されます。
Adam Badura 2016年

1
@AdamBadura:PROCESS SUBSTITUTION によって作成された特別なファイルからのwhile read ... done < <(find blah)通常のシェルリダイレクトです。これはパイプラインとは異なります。パイプラインはサブシェルでループを実行するため、その中に設定された変数は後続のコマンドで保持されないためです。<find blah | while read ... donewhile
dave_thompson_085 2016年

3

IFS = newline(ファイル名に改行が含まれていないと仮定)を使用できますが、置換の前に外部シェルで設定する必要があります。

$ ls -1
a file with spaces
able
alpha
baker
boo hoo hoo
bravo
$ # note semicolon here; it's not enough to be in the environment passed
$ # to printf, it must be in the environment OF THE SHELL WHILE PARSING
$ IFS=$'\n'; printf '%s\n' --afiles $(find . -name 'a*') --bfiles $(find . -name 'b*')
--afiles
./able
./a file with spaces
./alpha
--bfiles
./bravo
./boo hoo hoo
./baker

ではzshなくbashnull $'\0'を使用することもできます。のbashように決して使用されない十分に奇妙な文字がある場合でも、改行を処理できます

 IFS=$'\1'; ... $(find ... -print0 | tr '\0' '\1') ...

ただし、この方法では、@ steeldriverの回答のコメントで行った追加のリクエストは処理されません。


私がバッシュで理解しているように、強制的IFSに分割する方法はありませんnullか?
Adam Badura 2016年

@AdamBadura:私はそうではないと確信しています。bashは、IFSを含むすべての変数でnullバイトを許可しません。read -d ''steeldriverのメソッドで使用されるのは、nullバイトを含む空の文字列ではないことに注意してください。(そして、いずれにしても、コマンドオプションはvarではありません。)
dave_thompson_085 '24年

set -o noglobそのsplit + glob演算子を使用する前に、グロビング()を無効にする必要もあります(を除くzsh)。
ステファンChazelas


@AdamBaduraはい、bashで、ヌルは正確に同じであり$'\0'、また、など''
Isaac

1

なぜあなたが諦めたのか分かりませんxargs

そのためxargs、1つの検索からを実行しても、他の検索に対処する方法が残ります…

文字列--xyz-filesは多くの引数の1つに過ぎず、プログラムで解釈される前に特別であると考える理由はありません。私はあなたがxargs両方のfind結果の間でそれを通過することができると思います:

{ find -L some/dir -name \*.abc -print0 | sort -z; echo -ne "--xyz-files\0"; find -L other/dir -name \*.xyz -print0 | sort -z; } | xargs -0 ./program --abc-files

あなたが正しいです!これも機能します!ただし-print0、2番目に失敗したことに注意してくださいfind。また、この道を進んでいる場合、私は入れてしまうでしょう--abc-filesとしてechoだけでなく-ただ一貫性を保つために。
Adam Badura 2016年

このアプローチは、アレイのアプローチよりも単純で、やや単調に見えます。ただし、.abcファイルがない場合もないはずである--abc-files(と同じ.xyz)場合をカバーするために、追加のロジックが必要になります。アレイベースのソリューションによってsteeldriverはまたそれのための余分なロジックが必要ですが、それは、このソリューションの主な利点破壊ここではそれほど些細なことかもしれないが、そのロジックが簡単です-シンプルさを。
Adam Badura 2016年

また、私は本当にわからないが、私はそれが前提としxargs、引数を分割し、代わりに1のいくつかのコマンドを作ってみることは決してありませんしない限り、され、明示的にそうするように指示し-L--max-lines-l、)、 (--max-args-nまたは--max-chars-s)の引数。私は正しいですか?それともいくつかのデフォルトがありますか?私のプログラムはそのような分割を正しく処理せず、むしろそれを呼び出すことに失敗したので...
Adam Badura

1
@AdamBadura Missing- -print0修正、ありがとう。私はすべての答えを知っているわけではありませんが、私の解決策では追加のロジックを含めることが難しくなることに同意します。この方法を知った今、私はおそらく自分で配列に行くでしょう。私の答えは本当にあなたのためではありませんでした。あなたはすでに他の答えを受け入れており、あなたの問題は解決されたと思います。私は、複数のソースからを介して引数を渡すことができることを指摘したかっただけxargsでしたが、これは一見ではわかりませんでした。概念実証として扱うこともできます。今、私たちは皆、いくつかの異なるアプローチを知っており、私たちは意識的にあらゆる特定のケースで自分に合うものを選択することができます。
カミルMaciorowski 16年

はい、すでにアレイベースのソリューションを実装しており、魅力的に機能します。私は特に、それがオプション性をいかにきれいに処理するかを誇りに思っています(ファイルがない場合は、いいえ--abc-files)。しかし、あなたは正しい-あなたの選択肢を知るのは良いことです!特に、それは不可能だと間違って思っていた。
Adam Badura 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.