findの出力をループするのはなぜ悪い習慣ですか?


170

この質問は、

シェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされるのですか?

これらのコンストラクトが表示されます

for file in `find . -type f -name ...`; do smth with ${file}; done

そして

for dir in $(find . -type d -name ...); do smth with ${dir}; done

一部の人々がこの種のものを避けるべき理由を説明するそれらの投稿にコメントするために時間を割いても、ほぼ毎日ここで使用されています...
そのような投稿の数を見る(そしてそれらのコメントが時々無視されるという事実)私も質問をするかもしれないと思った:

なぜループfindアウトの出力が悪い習慣であり、返される各ファイル名/パスに対して1つ以上のコマンドを実行する適切な方法は何findですか?


12
これは、「lsの出力を解析しない!」のようなものだと思います。-確実にどちらか一方を個別に行うことができますが、それらは生産品質よりも迅速なハックです。または、より一般的には、決して独断的ではありません。
ブルースエディガー


これは、標準的な答えになってしなければならない
ザイド

6
findのポイントは、見つかったものをループすることです。
OrangeDog

2
補助的なポイント-出力をファイルに送信し、スクリプトの後半で処理することもできます。これにより、スクリプトをデバッグする必要がある場合にファイルリストを確認できます。
user117529

回答:


87

問題

for f in $(find .)

2つの互換性のないものを組み合わせます。

find改行文字で区切られたファイルパスのリストを出力します。$(find .)そのリストコンテキストで引用符を付けないままにしておくと呼び出されるsplit + glob演算子は、文字$IFS(デフォルトでは改行を含むが、スペースとタブ(およびNULを含むzsh))で文字を分割し、結果の各単語でグロビングを実行します(ただしin zsh)(およびksh93またはpdksh派生物のブレース展開も!)

あなたがそれを作ったとしても:

IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
              # but not ksh93)
for f in $(find .) # invoke split+glob

改行文字はファイルパス内のすべての文字と同じように有効であるため、それは依然として間違っています。の出力find -printは単純に後処理できません(ここに示すような複雑なトリックを使用する場合を除く)。

また、シェルfindはファイルのループを開始する前に、完全に出力を保存し、それをsplit + glob(その出力をメモリに2回保存することを意味します)する必要があることも意味します。

find . | xargs cmd同様の問題があることに注意してください(空白、改行、一重引用符、二重引用符、およびバックスラッシュ(および一部のxarg実装では有効な文字の一部を形成しないバイト)が問題です)

より適切な代替案

for出力でループを使用する唯一の方法は、以下をサポートfindするものを使用するzshことですIFS=$'\0'

IFS=$'\0'
for f in $(find . -print0)

(置き換える-print0-exec printf '%s\0' {} +するためにfind)最近は非標準(ただし、非常に一般的にサポートしない実装-print0)。

ここで、正しいポータブルな方法は次を使用すること-execです:

find . -exec something with {} \;

または、something複数の引数を取ることができる場合:

find . -exec something with {} +

そのファイルのリストをシェルで処理する必要がある場合:

find . -exec sh -c '
  for file do
    something < "$file"
  done' find-sh {} +

(複数起動する場合があることに注意してくださいsh)。

一部のシステムでは、次を使用できます。

find . -print0 | xargs -r0 something with

それは、標準的な構文と手段をほとんど利点があるもののsomethingさんをstdinいずれかのパイプですか/dev/null

使用したい理由の1つ-Pxargs、並列処理にGNU のオプションを使用することです。このstdin問題はxargs-aプロセス置換をサポートするシェルのオプションを使用してGNU で回避することもできます。

xargs -r0n 20 -P 4 -a <(find . -print0) something

たとえば、somethingそれぞれ20個のファイル引数を取る最大4つの同時呼び出しを実行します。

zshbash、の出力をループする別の方法はfind -print0です:

while IFS= read -rd '' file <&3; do
  something "$file" 3<&-
done 3< <(find . -print0)

read -d '' 改行で区切られたレコードではなく、NULで区切られたレコードを読み取ります。

bash-4.4また、上記で返されるファイルをfind -print0配列に保存することもできます。

readarray -td '' files < <(find . -print0)

zsh(保存の利点がある同等findの終了ステータスを):

files=(${(0)"$(find . -print0)"})

を使用するとzsh、ほとんどのfind式を再帰的なグロビングとグロブ修飾子の組み合わせに変換できます。たとえば、ループオーバーfind . -name '*.txt' -type f -mtime -1は次のようになります。

for file (./**/*.txt(ND.m-1)) cmd $file

または

for file (**/*.txt(ND.m-1)) cmd -- $file

--as の必要性に注意してください**/*、ファイルパスはで始まっていないため、たとえばで./始まって-います)。

ksh93そしてbash最終的にのためのサポートを追加**/(再帰グロブのない、より進歩フォームが)が、使用可能まだないグロブ予選**が非常に限られています。またbash、4.3より前は、ディレクトリツリーを下るときにシンボリックリンクに従うことに注意してください。

ループオーバーと同様に$(find .)、これはファイルのリスト全体をメモリ1に保存することも意味します。ただし、場合によっては、ファイルに対するアクションがファイルの検出に影響を与えたくない場合(最終的に検出される可能性のあるファイルをさらに追加する場合など)が望ましい場合があります。

その他の信頼性/セキュリティに関する考慮事項

レース条件

私たちは、信頼性の話をしている場合今、私たちは時間との間の競合状態を言及する必要がfind/ zshファイルを見つけ、それを基準にし、それが使用されている時間(満たしていることを確認しTOCTOUレースを)。

ディレクトリツリーを下るときでも、シンボリックリンクをたどらないようにし、TOCTOUレースなしでそれを行う必要があります。findfind少なくともGNU )はopenat()、適切なO_NOFOLLOWフラグ(サポートされている場合)を使用してディレクトリを開き、各ディレクトリのファイル記述子を開いたままにして、zsh/ bash/ kshしないでそれを行います。そのため、攻撃者が適切なタイミングでディレクトリをシンボリックリンクに置き換えることができた場合、間違ったディレクトリに降りることができます。

場合でもfindして、適切にディレクトリを降りない-exec cmd {} \;となおさらで-exec cmd {} +一度、cmdとして例えば、実行されないcmd ./foo/barcmd ./foo/bar ./foo/bar/baz、時間でcmdの使用を作る./foo/barの属性はbar、もはやマッチした基準を満たすことfindが、さらに悪化し、./fooあったかもしれません他の場所へのシンボリックリンクに置き換えられます(そして呼び出しウィンドウに十分なファイル-exec {} +があるのをfind待つ場所で、レースウィンドウがずっと大きくなりますcmd)。

一部のfind実装には-execdir、2番目の問題を軽減するための(非標準の)述語があります。

と:

find . -execdir cmd -- {} \;

find chdir()sを実行する前に、ファイルの親ディレクトリに移動しますcmd。代わりに呼び出すのではcmd -- ./foo/bar、それが呼び出されますcmd -- ./barcmd -- barしたがって、いくつかの実装で--)、これに伴う問題./fooのシンボリックリンクに変更されているが回避されます。これにより、rmより安全なコマンド(別のファイルを削除できますが、別のディレクトリ内のファイルは削除できません)が使用されますが、シンボリックリンクに従わないように設計されていない限り、ファイルを変更するコマンドは使用できません。

-execdir cmd -- {} +時には動作することもありますが、GNUのいくつかのバージョンを含むいくつかの実装findでは、と同等-execdir cmd -- {} \;です。

-execdir また、深すぎるディレクトリツリーに関連する問題のいくつかを回避できるという利点もあります。

に:

find . -exec cmd {} \;

与えられたパスのサイズは、cmdファイルがあるディレクトリの深さとともに大きくなります。そのサイズがPATH_MAX(Linuxの4kのような)より大きくcmdなると、そのパスで行うシステムコールはENAMETOOLONGエラーで失敗します。

では-execdir、ファイル名(接頭辞が付いている場合があります)のみ./がに渡されcmdます。ほとんどのファイルシステムのファイル名自体には、NAME_MAXよりもはるかに低い制限()がPATH_MAXあるため、ENAMETOOLONGエラーが発生する可能性は低くなります。

バイトと文字

また、find一般的なファイル名の処理に関するセキュリティを検討するときに見落とされることがよくあります。ほとんどのUnixライクシステムでは、ファイル名はバイトのシーケンスであるという事実です(ファイルパスのバイト値は0で、ほとんどのシステムでは( ASCIIベースのものは、今のところまれなEBCDICベースのものを無視します)0x2fはパス区切り文字です)。

これらのバイトをテキストと見なすかどうかを決定するのはアプリケーション次第です。そして、一般的には行われますが、一般的に、バイトから文字への変換は、環境に基づいてユーザーのロケールに基づいて行われます。

つまり、特定のファイル名はロケールに応じて異なるテキスト表現を持つ場合があります。たとえば、バイトシーケンス63 f4 74 e9 2e 74 78 74côté.txt、文字セットがISO-8859-1 cєtщ.txtであるロケール、および文字セットがIS0-8859-5 であるロケールでそのファイル名を解釈するアプリケーション用です。

悪い。文字セットがUTF-8(今日の標準)であるロケールでは、63 f4 74 e9 2e 74 78 74は単に文字にマッピングできませんでした!

findそのためのテキストとしてファイル名を考えてそのようなアプリケーションです-name/ -path(のような、より、述語-inameまたは-regexいくつかの実装では)。

つまり、たとえば、いくつかのfind実装(GNUを含むfind)を使用するということです。

find . -name '*.txt'

(バイトではなく0個以上の文字と一致63 f4 74 e9 2e 74 78 74する)UTF-8ロケールで呼び出された場合、上記のファイルはそれらの非文字*と一致しなかったため、見つかりません。

LC_ALL=C find... Cロケールは文字ごとに1バイトを意味し、(一般的に)すべてのバイト値が文字にマップされることを保証するので(一部のバイト値については未定義の場合もありますが)、この問題を回避します。

シェルからこれらのファイル名をループする場合、そのバイトと文字も問題になる可能性があります。この点に関して、主に4つの主な種類のシェルがあります。

  1. のようなまだマルチバイトを認識しないものdash。それらの場合、バイトは文字にマッピングされます。たとえば、UTF-8ではcôté4文字ですが、6バイトです。UTF-8が文字セットであるロケールでは、

    find . -name '????' -exec dash -c '
      name=${1##*/}; echo "${#name}"' sh {} \;
    

    findUTF-8でエンコードされた4文字で構成される名前のファイルは正常に見つかりますが、dash4〜24の範囲の長さを報告します。

  2. yash: 反対。文字のみを扱います。入力はすべて内部的に文字に変換されます。最も一貫性のあるシェルになりますが、任意のバイトシーケンス(有効な文字に変換されないバイトシーケンス)に対処できないことも意味します。Cロケールでも、0x7fを超えるバイト値には対応できません。

    find . -exec yash -c 'echo "$1"' sh {} \;
    

    UTF-8ロケールではcôté.txt、たとえば以前のISO-8859-1では失敗します。

  3. 以下のようなこれらbashまたはzshマルチバイトサポートところは徐々に追加されました。それらは、文字にマップできないバイトを、文字であるかのように考えることにフォールバックします。特にGBKやBIG5-HKSCSのようなあまり一般的ではないマルチバイト文字セットにはいくつかのバグがあります(マルチバイト文字の多くは0から127の範囲のバイトを含んでいるので非常に厄介です) )。

  4. shFreeBSD(少なくとも11)のようなもの、またはmksh -o utf8-modeマルチバイトをサポートするがUTF-8のみをサポートするもの。

ノート

1完全を期すために、zshリスト全体をメモリに保存せずに再帰的なグロビングを使用してファイルをループするハッキング方法に言及できます。

process() {
  something with $REPLY
  false
}
: **/*(ND.m-1+process)

+cmdは、cmd現在のファイルパスを使用して(通常は関数)を呼び出すglob修飾子です$REPLY。この関数はtrueまたはfalseを返し、ファイルを選択するかどうかを決定します(また$REPLY$reply配列内の複数のファイルを変更または返すこともあります)。ここでは、その関数で処理を行い、ファイルが選択されないようにfalseを返します。


zshのとbashが使用可能な場合は、ありちょうどグロブとシェル構文を使用しての代わりしかめるしようとしたほうが良いfind安全行動に。グローブはデフォルトで安全ですが、findはデフォルトでは安全ではありません。
ケビン

@Kevin、編集を参照してください。
ステファンシャゼル

182

findの出力をループするのは悪い習慣なのはなぜですか?

簡単な答えは:

ファイル名には任意の文字を含めることができるためです。

したがって、ファイル名を区切るために確実に使用できる印刷可能な文字はありません。


改行がされていることが多いですので、ファイル名を区切るために(不正に)使用異例のファイル名に改行文字を含めること。

ただし、任意の仮定に基づいてソフトウェアを構築すると、せいぜい異常なケースを処理できず、最悪の場合、システムの制御を奪う悪意のある悪用にさらされることになります。したがって、堅牢性と安全性の問題です。

2つの異なる方法でソフトウェアを作成でき、そのうちの1つがエッジケース(異常な入力)を正しく処理し、もう1つが読みやすい場合、トレードオフがあると主張するかもしれません。(私はしたくない。正しいコードを好む。)

ただし、適切で堅牢なバージョンのコード読みやすい場合は、エッジケースで失敗するコードを記述する理由はありません。これは、find見つかった各ファイルでコマンドを実行する必要がある場合です。


具体的に見てみましょう。UNIXまたはLinuxシステムでは、ファイル名にa /(パスコンポーネントの区切り文字として使用される)以外の文字を含めることができ、ヌルバイトを含めることはできません。

したがって、ファイル名を区切る唯一の正しい方法はヌルバイトです。


GNUのためfind含み-print0、それは印刷ファイル名を区切るためにヌル・バイトを使用する一次、GNUがfind でき、安全にGNUで使用することがxargs、その-0フラグ(と-rの出力を処理するためのフラグ)find

find ... -print0 | xargs -r0 ...

ただし、次の理由により、このフォームを使用する正当な理由はありません。

  1. GNU findutilsへの依存関係が追加されますが、そこにある必要はありません。
  2. findされて設計され、それが検出されたファイル上でコマンドを実行できるようにします。

また、GNU はとをxargs必要-0としますが-r、FreeBSD xargsは必要なだけで-0-rオプションはありません)、一部xargsはまったくサポート-0していません。ですから、POSIXの機能find(次のセクションを参照)に固執してスキップするのが最善ですxargs

ポイント2— findが見つけたファイルに対してコマンドを実行する機能については、マイクルーキデスが最もうまく言ったと思います。

findのビジネスは式を評価しています。ファイルを見つけることではありません。はい、find確かにファイルを見つけます。しかし、それは本当に単なる副作用です。

--Unix Power Tools


POSIX指定の用途 find

find結果に対して1つ以上のコマンドを実行する適切な方法は何ですか?

見つかったファイルごとに1つのコマンドを実行するには、次を使用します。

find dirname ... -exec somecommand {} \;

見つかったファイルごとに複数のコマンドを順番に実行するには、最初のコマンドが成功した場合にのみ2番目のコマンドを実行する必要があります。

find dirname ... -exec somecommand {} \; -exec someothercommand {} \;

一度に複数のファイルで単一のコマンドを実行するには:

find dirname ... -exec somecommand {} +

find と組み合わせて sh

出力のリダイレクトやファイル名などの拡張子の除去など、コマンドでシェル機能を使用する必要がある場合は、sh -c構成を使用できます。これについていくつか知っておく必要があります。

  • コードに{}直接埋め込まないでくださいsh。これにより、悪意を持って作成されたファイル名から任意のコードを実行できます。また、実際に動作することはPOSIXによっても指定されていません。(次のポイントを参照してください。)

  • {}複数回使用したり、長い引数の一部として使用したりしないでください。 これは移植性がありません。たとえば、これをしないでください:

    find ... -exec cp {} somedir/{}.bak \;

    以下のPOSIX仕様findを引用するに

    utility_nameまたは引数文字列に2つの文字 "{}"が含まれるが、2つの文字 "{}"が含まれない場合、findがこれらの2つの文字を置き換えるか、変更せずに文字列を使用するかは実装定義です。

    ... 2つの文字 "{}"を含む複数の引数が存在する場合、動作は指定されていません。

  • -cオプションに渡されたシェルコマンド文字列に続く引数は、で始まる$0シェルの位置パラメーターに設定されます。で始まらない$1

    このため、生成されたシェル内からのエラー報告に使用されるの$0ような「ダミー」値を含めることをおfind-sh勧めします。また、これにより、"$@"複数のファイルをシェルに渡す場合などの構成を使用できますが、の値を省略$0すると、最初に渡されるファイルがに設定され$0、に含まれなくなり"$@"ます。


ファイルごとに単一のシェルコマンドを実行するには、次を使用します。

find dirname ... -exec sh -c 'somecommandwith "$1"' find-sh {} \;

ただし、通常は、シェルループでファイルを処理する方がパフォーマンスが向上するため、検出されたすべてのファイルに対してシェルが生成されることはありません。

find dirname ... -exec sh -c 'for f do somecommandwith "$f"; done' find-sh {} +

(各位置パラメータとfor f do同等でfor f in "$@"; doあり、順番に処理することに注意してください。つまり、find名前の特殊文字に関係なく、で見つかった各ファイルを使用します。)


正しいfind使用法のさらなる例:

(注:このリストは自由に拡張してください。)


5
各ファイルの現在のシェルでfindコマンドを実行する必要がある場合(変数を設定する場合など)、出力の解析に代わるものがわからない場合が1つあります。この場合、私が知っている最高のイディオムです。注:移植性はありません。bashまたはzshを使用してください。また、ループ内の何かが標準入力を読み取ろうとする場合に備えて、and があります。while IFS= read -r -u3 -d '' file; do ... done 3< <(find ... -print0)<( )-u33<
ゴードンデイヴィソン

1
@GordonDavisson、おそらく—しかし、これらの変数何に設定する必要がありますか?私は、それが何であれ、コールで処理されるべきだと主張しfind ... -execます。または、ユースケースを処理する場合は、シェルグロブを使用します。
ワイルドカード

1
多くの場合、ファイルの処理後に要約を出力したい(「2変換、3スキップ、次のファイルにエラーがありました:...」)、それらのカウント/リストはシェル変数に蓄積する必要があります。また、ファイル名の配列を作成して、順番に繰り返すよりも複雑なことを行えるようにする場合もあります(その場合はfilelist=(); while ... do filelist+=("$file"); done ...)。
ゴードンデイヴィソン

3
あなたの答えは正しいです。しかし、私は教義が好きではありません。私がよく知っているとしても、安全で、find出力のループを入力するのが簡単であるか、さらに悪いことにを使用するとさらに悪い(多くの場合、インタラクティブな)ユースケースがありlsます。私はこれを毎日問題なく行っています。あらゆる種類のツールの-print0、-null、-z、または-0オプションについて知っています。しかし、本当に必要でない限り、対話型シェルプロンプトでそれらを使用するのに時間を無駄にしないでしょう。これはあなたの答えにも記載されています。
ルディミエ

16
@rudimeier、ドグマ対ベストプラクティスの議論はすでに死に至っています。興味がない。あなたがそれをインタラクティブに使用し、それがうまく、うまくいくなら、しかし私はそれをすることを促進するつもりはありません。堅牢なコードが何であるかを学習してから、実稼働スクリプトを作成するときにそれだけを行うことに気を使うスクリプト作成者の割合は、インタラクティブに行うことに慣れていることを行うのではなく、非常にわずかです。処理は、常にベストプラクティス促進することです。人々は物事を行う正しい方法があること
ワイルドカード

10

この回答は非常に大きな結果セットに対するもので、主にパフォーマンスに関するものです。たとえば、低速のネットワークを介してファイルのリストを取得する場合です。少量のファイル(たとえば、ローカルディスク上で数百または1000程度)の場合、これのほとんどは意味がありません。

並列処理とメモリ使用量

分離問題などに関連する他の回答とは別に、別の問題があります

for file in `find . -type f -name ...`; do smth with ${file}; done

改行で分割される前に、バックティック内の部分を最初に完全に評価する必要があります。これは、大量のファイルを取得した場合、さまざまなコンポーネントにサイズ制限がある場合に窒息する可能性があることを意味します。制限がない場合、メモリが不足する可能性があります。いずれにせよ、リスト全体が出力されfindてから解析されるまで待ってからfor最初のを実行する必要がありますsmth

推奨されるUNIXの方法は、本質的に並列に実行され、一般的に任意の巨大なバッファーを必要としないパイプで作業することです。つまり、findをに並行して実行しsmth、現在のファイル名のみをRAMに保持し、それをに渡すことを好むでしょうsmth

そのための少なくとも部分的にOKな解決策の1つは、前述のものfind -exec smthです。これにより、すべてのファイル名をメモリに保持する必要がなくなり、適切に並列実行されます。残念ながら、smthファイルごとに1 つのプロセスも開始します。smth1つのファイルのみで機能する場合は、それが必要な方法です。

もし可能ならば、最適解は次のようになりますfind -print0 | smthと、smthそのSTDIN上のファイル名を処理することができるという。次にsmth、ファイルの数に関係なく1つのプロセスのみがあり、2つのプロセス間でバッファリングする必要があるのは、わずかなバイト(組み込みのパイプバッファリングが何であれ)です。もちろん、これはsmth標準のUnix / POSIXコマンドである場合、かなり非現実的ですが、自分で作成する場合のアプローチかもしれません。

それが不可能な場合find -print0 | xargs -0 smth、おそらく、より良い解決策の1つです。コメントで@ dave_thompson_085が言及したように、システムの制限に達すると(デフォルトでは128 KBの範囲またはシステムに課せられる制限で)、xargs複数の実行に渡って引数を分割しsmthます。execファイルはの1回の呼び出しに渡されるsmthため、smthプロセス数と初期遅延のバランスを見つけます。

編集:「最高」の概念を削除-より良いものが現れるかどうかを言うのは難しいです。;)


find ... -exec smth {} +解決策です。
ワイルドカード

find -print0 | xargs smthまったく機能しませんが、find -print0 | xargs -0 smth(注-0)またはfind | xargs smthファイル名に空白引用符やバックスラッシュがない場合はsmth、使用可能な数のファイル名で1つの引数リストに収まります。maxargsを超えると、smth指定されたすべての引数を処理するために必要な回数だけ実行されます(制限なし)。を使用して、より小さな「チャンク」(したがって、以前の並列処理)を設定でき-L/--max-lines -n/--max-args -s/--max-charsます。
-dave_thompson_085


4

理由の1つは、空白が動作中にスパナを投げ、ファイル「foo bar」が「foo」および「bar」として評価されるようにすることです。

$ ls -l
-rw-rw-r-- 1 ec2-user ec2-user 0 Nov  7 18:24 foo bar
$ for file in `find . -type f` ; do echo filename $file ; done
filename ./foo
filename bar
$

代わりに-execが使用されていれば問題ありません

$ find . -type f -exec echo filename {} \;
filename ./foo bar
$ find . -type f -exec stat {} \;
  File: ‘./foo bar’
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: ca01h/51713d    Inode: 9109        Links: 1
Access: (0664/-rw-rw-r--)  Uid: (  500/ec2-user)   Gid: (  500/ec2-user)
Access: 2016-11-07 18:24:42.027554752 +0000
Modify: 2016-11-07 18:24:42.027554752 +0000
Change: 2016-11-07 18:24:42.027554752 +0000
 Birth: -
$

特に、findすべてのファイルでコマンドを実行するオプションがあるため、簡単に最適なオプションです。
Centimane

1
また、考慮-exec ... {} \;-exec ... {} +
thrig

1
使用for file in "$(find . -type f)" echo "${file}"て、空白でも動作する場合、他の特殊文字はより多くの問題を引き起こすと思います
-mazs

9
@mazs-いいえ、引用はあなたが思うことをしません。いくつかのファイルがあるディレクトリで、for file in "$(find . -type f)";do printf '%s %s\n' name: "${file}";doneどれが(あなたによると)で始まる別の行に各ファイル名を印刷するかを試してくださいname:。そうではありません。
-don_crissti

2

コマンドの出力は単一の文字列ですが、ループをループするには文字列の配列が必要です。それが「機能する」理由は、シェルがあなたのために空白文字列をひそかに分割するからです。

第二に、の特定の機能が必要な場合を除きfind、シェルはすでにそれ自体で再帰的なグロブパターンを展開できる可能性が最も高く、決定的に、適切な配列に展開することに注意してください。

バッシュの例:

shopt -s nullglob globstar
for i in **
do
    echo «"$i"»
done

魚でも同じ:

for i in **
    echo «$i»
end

の機能が必要な場合はfind、NUL(find -print0 | xargs -r0イディオムなど)でのみ分割してください。

Fishは、NUL区切りの出力を繰り返すことができます。したがって、これは実際に悪くありません

find -print0 | while read -z i
    echo «$i»
end

最後の小さな落とし穴として、多くのシェル(もちろんFishではありません)では、コマンド出力をループすると、ループ本体がサブシェルになります(つまり、ループの終了後に表示される変数を設定することはできません)。決してあなたが欲しいもの。


@don_crissti正確に。それはしません、一般的に動作します。私はそれが「動く」(引用符で)と言って皮肉にしようとしていました。
user2394284

再帰的グロビングはzsh90年代初期に発生したことに注意してください(ただし、必要な場合**/*があります)。fishただし、bashの同等の機能の以前の実装のように、ディレクトリツリーを下るときはシンボリックリンクに従います。実装間の違いについては、ls *、ls **お​​よびls ***の結果を参照してください。
ステファンシャゼル

1

findの出力をループすることは悪い習慣ではありません。(このような状況での)悪い習慣は、入力が特定の形式であることを認識する(テストおよび確認する)のではなく、特定の形式であると想定することです。

tldr / cbf: find | parallel stuff

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