web-dev-qa-db-ja.com

グロブを「検索」に変換

私は何度も何度もこの問題を抱えていました。正しいファイルと正確に一致するグロブがありますが、Command line too longが発生します。特定の状況で機能するfindgrepの組み合わせに変換するたびに、100%の等価ではありません。

例えば:

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

グロブをfind式に変換するためのツールがありませんか?または、サブディレクトリで同じグロブと一致せずにグロブと一致するfindのオプションはありますか(たとえば、foo/*.jpgbar/foo/*.jpgとの一致が許可されていません)?

11
Ole Tange

Argument-list-is-too-longエラーが発生することが問題である場合は、ループまたは組み込みのシェルを使用します。 _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の glob2regpat() とPythonの fnmatch.translate() はどちらもグロブを正規表現に変換できますが、どちらも_.*_ for _*_、_/_全体で一致。

15
muru

あなたはあなたの要件に一致する検索のための正規表現を書くことができます:

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

私の他の回答 に関する注記を一般化すると、質問に対するより直接的な回答として、このPOSIX 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 . "$@"

one標準shグロブで使用するため(brace展開を使用する例の2つのグロブではない ):

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

(これは、...以外のドットファイルまたはドットディレクトリを無視せず、ファイルのリストをソートしません)。

これは.または..コンポーネントなしで、現在のディレクトリに関連するグロブでのみ機能します。少し努力すれば、グロブよりも任意のグロブに拡張できます... glob2find 'dir/*'dirを検索しないように最適化することもできます。パターン。

0