globを「検索」に変換

globを「検索」に変換

私はこの問題に直面しています:正しいファイルと正確に一致するglobがありますが、Command line too long結果はfindgrep

たとえば、

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

findglobを私が知らない式に変換できるツールはありますか?それともfind、サブディレクトリに同じglobを一致させずにglobを一致させるオプションはありますか(例:foo/*.jpg一致は許可されていませんbar/foo/*.jpg)?

答え1

引数リストが長すぎるというエラーが発生する問題がある場合は、ループまたは組み込みシェルを使用してください。command glob-that-matches-too-much間違っている可能性がありますが、そうではfor f in glob-that-matches-too-muchありません。したがって、次のようにすることができます。

for f in foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
do
    something "$f"
done

ループは非常に遅いかもしれませんが、うまくいくはずです。

または:

printf "%s\0" foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg |
  xargs -r0 something

printf上記はほとんどのシェルに組み込まれているため、システムコールの制限を解決できますexecve()。)

$ cat /usr/share/**/* > /dev/null
zsh: argument list too long: cat
$ printf "%s\n" /usr/share/**/* | wc -l
165606

bashでも動作します。これが正確にどこに文書化されているのかよくわかりません。


Vim 2個glob2regpat()そしてPythonfnmatch.translate()globは正規表現に変換できますが、両方とも.*for *、acrossを使用します/

答え2

find-name/-path標準述部の場合)globと同じようにワイルドカードパターンを使用します(これは{a,b}glob演算子ではありません。拡張後に2つのglobを取得します)。主な違いはスラッシュ処理です。ドットファイルとディレクトリはで特に処理されませんfind*globs は複数のディレクトリにまたがっていません。*/*/*最大2つのレベルのディレクトリが一覧表示されます。を追加すると、-path './*/*/*'少なくとも3つのレベルの深さのすべてのファイルと一致し、findすべての深さのディレクトリ内容の一覧表示は中断されません。

その特定のために

./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg

翻訳しやすいいくつかのグローブ、深さ3のディレクトリが必要なので、次のことができます。

find . -mindepth 3 -maxdepth 3 \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

またはPOSIXとして:

find . -path './*/*/*' -prune \
       \( -path './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' -o \
          -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg' \) \
       -exec cmd {} +

*これにより、対応する文字と?一致しない文字が確認されます/

find、 glob とは異なり、現在のディレクトリの内容ではないディレクトリの内容を読み取ってファイルのリストをfoo*bar並べ替えません。ただし、内容を無視/一致したり、[A-Z]無効な文字の動作に関する質問が指定されていない場合は、同じ結果が得られます。ファイルリスト) 。*?

しかし、とにかく、@muruが言った。find、システムコールの制限を解決するためにファイルリストを複数の実行に分割することであれば、それにexecve()頼る必要はありません。zsh(with zargs)やksh93(with)などの一部のシェルには、command -xこれをサポートする機能が組み込まれています。

With zsh(globには同等物-type fと他のほとんどの述部findもあります)たとえば、次のようになります。

autoload zargs # if not already in ~/.zshrc
zargs ./foo*bar/quux[A-Z](|.bak)/pic[0-9][0-9][0-9][0-9]?.jpg(.) -- cmd

(|.bak)glob演算子の逆です{,.bak}。glob(.)修飾子はfind'sと同じです。ドットファイルを含めるためにwithなどのソートをスキップするために-type f追加されました(このglobには適用されません)。oNfindD


find1 glob などのディレクトリツリーをクロールするには、次のものが必要です。

find . ! -name . \( \
  \( -path './*/*' -o -name 'foo*bar' -o -prune \) \
  -path './*/*/*' -prune -name 'pic[0-9][0-9][0-9][0-9]?.jpg' -exec cmd {} + -o \
  \( ! -path './*/*' -o -name 'quux[A-Z]' -o -name 'quux[A-Z].bak' -o -prune \) \)

それは打つを除くすべてのレベル1ディレクトリと、foo*barまたはを除くすべてのレベル2ディレクトリを選択し、レベル3ディレクトリを選択して、そのレベルのすべてのディレクトリをクリーンアップします。quux[A-Z]quux[A-Z].bakpic...

答え3

要件に合ったコンテンツを見つけるために正規表現を書くことができます。

find . -regextype egrep -regex './foo[^/]*bar/quux[A-Z](\.bak)?/pic[0-9][0-9][0-9][0-9][^/]?\.jpg'

答え4

コメントのまとめ私の他の答え、質問に対するより直接的な答えとして、次のPOSIXスクリプトを使用してglobを式shに変換できます。find

#! /bin/sh -
glob=${1#./}
shift
n=$#
p='./*'

while true; do
  case $glob in
    (*/*)
      set -- "$@" \( ! -path "$p" -o -path "$p/*" -o -name "${glob%%/*}" -o -prune \)
      glob=${glob#*/} p=$p/*;;
    (*)
      set -- "$@" -path "$p" -prune -name "$glob"
      while [ "$n" -gt 0 ]; do
        set -- "$@" "$1"
        shift
        n=$((n - 1))
      done
      break;;
  esac
done
find . "$@"

の使用で一つ標準shグローブ(例で使用されている2つのグローブではありません)支柱の拡張):

glob2find './foo*bar/quux[A-Z].bak/pic[0-9][0-9][0-9][0-9]?.jpg' \
  -type f -exec cmd {} +

.(およびを除くドットファイルまたはドットディレクトリは無視されず、..ファイルリストはソートされません。)

.この方法は、コンポーネントがないかコンポーネントがない現在のディレクトリに関連するグローバル変数でのみ機能します..。少しの努力をすれば、1つのglobではなくすべてのglobに拡張でき、glob2find 'dir/*'パターンと同じものを見つけないように最適化することもできます。dir

関連情報