グロブを「検索」に変換


11

私は何度も何度もこの問題を抱えていました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/*.jpg、一致は許可されませんbar/foo/*.jpg)?


ブレースを展開すると、結果の式を-pathまたはで使用できるようになります-ipathfind . -path './foo*bar/quux[A-Z]/pic[0-9][0-9][0-9][0-9]?.jpg'動作するはず/fooz/blah/bar/quuxA/pic1234d.jpgですが、一致する場合を除きます。それは問題でしょうか?
muru

はい、それは問題になります。100%相当でなければなりません。
Ole

問題は、正確には何が違うのかわからないことです。あなたのパターンはかなり大丈夫です。
peterh-モニカを2017年

質問への回答として拡張機能の投稿を追加しました。それほど悪くないことを願っています。
peterh-モニカを2017年

あなたが行うことはできませんecho <glob> | catエコーは、bashの私の知識を仮定して、ビルドであるため、最大コマンドの制限はありません
Ferrybig

回答:


15

問題が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()両方がglobを正規表現に変換できますが、両方とも.*for *に一致するを使用し/ます。


それが本当であれば、交換somethingしてechoそれを行うべきです。
Ole Tange

1
@OleTangeそれが私が提案した理由ですprintf- echo何千回も呼び出すよりも速くなり、より柔軟になります。
muru 2017

4
を介して渡すことができる引数には制限がありexecますcat。これは、などの外部コマンドに適用されます。ただし、その制限は、などのシェル組み込みコマンドには適用されませんprintf
Stephen Kitt 2017

1
@OleTange printfは組み込みであるため、この行は長すぎません。シェルは、の引数を列挙するために使用するのと同じメソッドを引数に提供するために使用しますforcat組み込みではありません。
muru 2017

1
技術的には、mkshwhere printfがビルトインではないシェルやwhere がビルトインksh93catある(またはビルトインできる)シェルなどがあります。に頼らずに回避するにzargsはin も参照してください。zshxargs
ステファンシャゼラス

9

find-name/ -path標準述語の場合)は{a,b}、グロブと同じようにワイルドカードパターンを使用します(グロブ演算子ではないことに注意してください。展開後、2つのグロブが得られます)。主な違いは、スラッシュの処理(およびで特別に扱われないドットファイルとディレクトリfind)です。*グロブではいくつかのディレクトリにまたがることはありません。*/*/*最大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 {} +

(または-depth 3いくつかのfind実装)。またはPOSIXly:

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グロブとは逆foo*barに、現在のディレクトリにあるディレクトリ以外のディレクトリの内容を読み取り、ファイルのリストをソートしません。ただし、無効な文字に関する/の一致[A-Z]または動作が問題である場合を除いて、未指定の場合、同じファイルのリストが表示されます)。*?

しかし、いずれにせよ、@ muruが示したようfindに、execve()システムコールの制限を回避するために、ファイルのリストを複数の実行に分割するためだけであるかどうかに頼る必要はありません。zsh(with zargs)やksh93(with command -x)のような一部のシェルは、組み込みのサポートさえ持っています。

With zsh(これらのグロブには同等の、-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)へのグロブ演算子反している{,.bak}(.)と同等です修飾子グロブfinds「は-type f、追加oNと同じように並べ替えスキップし、そこにfindD-ファイルをドット(このグロブには適用されません)を含むように)


¹ findクロールする塊のようなディレクトリツリーは、次のようなものが必要だろう。

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の全てquux[A-Z]またはquux[A-Z].bak選択したもの、及びpic...レベル3でのものを(そしてそのレベルですべてのディレクトリをプルーニング)。


3

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

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

人為的エラーを回避するためにこの変換を行うツールはありますか?
Ole

いいえ、しかし、唯一の私が作った変更脱出していた.、のためのオプションの試合を追加.bak、変更*することが[^/]*/ fooの/ fooの/バーなどのようなパスと一致しないために
sebasth

しかし、あなたの変換でさえ間違っています。?は[^ /]に変更されません。これはまさに私が避けたいヒューマンエラーの一種です。
オレ丹下

1
私はegrepをと考えて、あなたが短縮することができます[0-9][0-9][0-9][0-9]?[0-9]{3,4}
wjandrea


0

あなたの質問に対するより直接的な答えとして、他の答えのメモを一般化すると、この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 . "$@"

で使用する1軒の標準shのglob(そうではない使用していますあなたの例の2つの塊ブレース展開を):

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

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

その1つは、現在のディレクトリに関連するグロブでのみ機能し...コンポーネントはありません。少し努力すれば、グロブ以外の任意のグロブに拡張できます。これは、パターンの場合と同じglob2find 'dir/*'ように見えないように最適化することもできdirます。

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