
私はこの問題に直面しています:正しいファイルと正確に一致するglobがありますが、Command line too long
結果はfind
。grep
たとえば、
./foo*bar/quux[A-Z]{.bak,}/pic[0-9][0-9][0-9][0-9]?.jpg
find
globを私が知らない式に変換できるツールはありますか?それとも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には適用されません)。oN
find
D
find
1 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].bak
pic...
答え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