シェルを使用せずに*グロブファイル

Aug 22 2020

特定のサブディレクトリ内のファイルを一覧表示したいのですがdocker exec、Dockerコンテナー内の一部として一覧表示しているので、本当に必要のないシェルをわざわざ起動したくありません。シェルだけでなく、単純なコマンドラインツールを使用してglobに一致するものをすべて見つけることは可能ですか?

たとえば、私の現在の呼び出しはbash -l -c 'echo /usr/local/conda-meta/*.json'です。一般的に利用可能なツールを使用してこれを単純化して、のようなものglobber /usr/local/conda-meta/*.jsonを作成することは可能ですか?これははるかに単純で軽量です。

回答

16 StéphaneChazelas Aug 23 2020 at 05:12

shシンプルで一般的に入手可能です。多くの言語のshようなものでコマンドラインを解析するために呼び出されるツールですsystem(cmdline)。一部のGNUを含む多くのOSは、コマンドラインの解析とPOSIXスクリプトの解釈という単純なことを行うには肥大化したため、bash実装に(GNUシェル)の使用を停止しました。shsh

あなたのbash -l -c 'echo /usr/local/conda-meta/*.json'コマンドラインは、おそらくによって解釈されているshすでに呼び出し。だからおそらくあなたはただすることができます:

printf '%s\n' /usr/local/conda-meta/*.json

直接。そうでない場合:

sh -c 'printf "%s\n" /usr/local/conda-meta/*.json'

findここでも使用できます。findグロブは行いませんが、シェルのものと同様のパターンに一致するファイル名を報告できます。

LC_ALL=C find /usr/local/conda-meta/. ! -name . -prune -name '*.json'

またはいくつかのfind実装で:

LC_ALL=C find /usr/local/conda-meta -mindepth 1 -maxdepth 1 -name '*.json'

(現在のロケールで有効な文字を形成しているバイトだけでなく、バ​​イトのシーケンスに一致するLC_ALL=Cようにここで必要なの*はシェル構造です。そのコマンドラインがシェルによって解釈されない場合は、次のように変更する必要があります。env LC_ALL=C find...

シェルグロブとのいくつかの違い:

  • ファイルのリストはソートされていません
  • 隠しファイルが含まれています(! -name '.*'それらを除外するためにを追加できます)
  • 一致するファイルがない場合、出力は得られません。グロブには、パターンをそのままにしておくという誤った機能があります。その場合、展開されません。
  • 最初の(標準)バリアントでは、ファイルはとして出力され/usr/local/conda-meta/./file.jsonます。
  • のようないくつかのグロブx*/y/../*zは簡単に翻訳されません(その場合、ディレクトリへのシンボリックリンクに関して異なる動作に注意してください)。

いずれの場合も、を使用echoして任意のデータを出力することはできません。

私の次の質問は、その出力をどうするかということです。を使用するとecho、SPC文字で区切られたファイルパスが出力され、myprintf以上のfind場合はNL文字で区切られます。NLSPCは両方ともファイル名で完全に有効な文字であるため、これらの出力は後処理で信頼できません。あなたは使用することができます'%s\0'の代わりに'%s\n'(または使用find-print0サポートされている場合)、ユーザーへの表示に適した、しかし後処理可能ではありません、。

効率の観点から、Ubuntu 20.04 /bin/sh(ダッシュ0.5.10.2)とそのfind(GNU find4.7.0)を比較します。

起動時間:

$ time (repeat 1000 sh -c '') ( repeat 1000; do; sh -c ''; done; ) 0.91s user 0.66s system 105% cpu 1.483 total $ time (repeat 1000 find . -quit)
( repeat 1000; do; find . -quit; done; )  1.35s user 1.25s system 103% cpu 2.507 total

いくつかのjsonファイルをグロブする:

$ TIMEFMT='%U user %S system %P cpu %*E total' $ time (repeat 1000 sh -c 'printf "%s\n" /usr/share/iso-codes/json/*.json') > /dev/null
0.95s user 0.72s system 105% cpu 1.587 total
$ time (repeat 1000  find /usr/share/iso-codes/json -mindepth 1 -maxdepth 1 -name '*.json') > /dev/null
1.34s user 1.35s system 103% cpu 2.599 total

でもbashよりほとんど遅くなりfind、ここで:

$ time (repeat 1000 bash -c 'printf "%s\n" /usr/share/iso-codes/json/*.json') > /dev/null
1.53s user 1.36s system 102% cpu 2.808 total

もちろん、YMMVは、システム、実装、それぞれのユーティリティのバージョン、およびそれらがリンクされているライブラリによって異なります。

歴史上の注意として、globの名前は、実際にはglob70年代初頭のUnixの最初のバージョンで呼び出されたユーティリティの名前に由来しています。これは、ワイルドカードパターンを拡張するためのヘルパーとしてに配置され/etc、呼び出されshました。

あなたはそのような非常に古いシェルを復活させるためにオンラインでいくつかのプロジェクトを見つけるでしょう https://etsh.nl/。考古学の演習として、globそこからユーティリティを構築して、次のことができるようになります。

glob printf '%s\n' '/usr/local/conda-meta/*.json'

ただし、警告に関するいくつかの注意事項があります。

  • それらは古代のグロブであり、[!x](ましてや[^x])サポートされていません。
  • それは8ビット安全ではありません。実際には、8ビット目のために使用されているエスケープグロブ演算子を($'\xe9*'同じものと一致しますi*$'\xaa*'で始まるファイル名にマッチします*;シェルが起動する前に引用された文字に対してその8番目のビットを設定しますglob
  • [a-f]照合順序ではなくバイト値の一致などの範囲(実際には、これは一般的にIMOの利点です)。
  • 一致しないグロブはNo matchエラーになります(ここでも、おそらく好ましくは、70年代後半にBourneシェルによって破壊されたものです)。

このglob機能は、70年代後半にPWBシェルとBourneシェルからシェルに移されました。その後、いくつかのfnmatch()glob()機能がその機能を他のアプリケーションから使用できるようにするためにCライブラリに追加されましたが、私はその関数への裸のインターフェースである標準でも一般的な有用性を認識していませんよ。でもperl呼び出すために使用cshglobパターンを拡張するために、その初期の頃に。

7 BasileStarynkevitch Aug 23 2020 at 14:50

シェル使用せずにファイルグロブする

読むべき明らかなドキュメントはglob(7)です。

fnmatch(3)、glob(3)、nftw(3)、stat(2)、readdir(3)を呼び出すCプログラムを作成または使用できます。

あなたのコード場合ガイル、パイソン、ゴー、錆、OCamlで、Common Lispの(例えばSBCL)...あなたは、同様の機能を見つけることができます。C ++で、POCOとQtを調べます。

Linuxシステムを使用していると想定しています。ところで、私のインタラクティブシェルはzshです(オートコンプリート機能が私見に適しています)。