拡張glob * /を!(* /)で無効にできないのはなぜですか?

拡張glob * /を!(* /)で無効にできないのはなぜですか?

extglobBashでシェルオプションを有効にすると、negate glob(from man bash)などの素晴らしい操作を実行できます。

   If  the  extglob shell option is enabled using the shopt builtin, sev‐
   eral extended pattern matching operators are recognized.  In the  fol‐
   lowing  description,  a pattern-list is a list of one or more patterns
   separated by a |.  Composite patterns may be formed using one or  more
   of the following sub-patterns:

          ?(pattern-list)
                 Matches zero or one occurrence of the given patterns
          *(pattern-list)
                 Matches zero or more occurrences of the given patterns
          +(pattern-list)
                 Matches one or more occurrences of the given patterns
          @(pattern-list)
                 Matches one of the given patterns
          !(pattern-list)
                 Matches anything except one of the given patterns

たとえば、

$ ls
a1file  a2file  b1file  b2file

$ shopt -s extglob
$ ls !(a*)
b1file  b2file

これで、ディレクトリで特定のタスクのみを実行したい場合は、次のものを使用できます*/

$ ls -F
a1file  a2file  adir/  b1file  b2file  bdir/

$ ls -d */
adir/  bdir/

しかし、globは*/明らかに否定できません。

$ ls -Fd !(*/)
a1file  a2file  adir/  b1file  b2file  bdir/

何を提供しますか?ディレクトリだけが正しく含まれているのに!(*/)ディレクトリを除外しないのはなぜですか?*/Bashでglobを使用してディレクトリを除外する方法はありますか?

上記のコマンドは、GNU bashバージョン5.1.8(1)リリースを使用してArch Linuxシステムでテストされました。

答え1

/球体が国境を越えないからです。特別な場合を除いて**/(もともとzsh今、他の殻にも見られます。通常、オプション(shopt -s globstarbashの場合)を設定した後、glob演算子は/ディレクトリリストに適用されるため、aを含む任意の項目と一致することはできません。

シェルはsで球を分割しますx/y/z。各コンポーネントについて、コンポーネントにglob演算子が含まれている場合、シェルは親ディレクトリを一覧表示し、各エントリのパターンを再一致させます。それ以外の場合は、²のあるファイル/のみを探します。lstat()

a*b/c一致するものがないことがわかりますa/b/c。シェルはa*b現在のディレクトリのエントリのみを一致します。 Evenは別々に[a/b]*扱われます。[ab]*/

*/それは存在し*、それとは別に存在するものはありません/*/xこれは、シェルが最初に現在のディレクトリのリストと一致するすべてのファイルを探し、次に各ファイルに名前が付けられたファイルが存在することを確認しようとする特別なケース*ですfile/x。 (ディレクトリまたはディレクトリへのシンボリックリンクの場合にのみ正しい)。lstat()x*/file/file

/ksh-style @(...), ... 拡散演算子 (または で使用できる!(...)サブセット ) 内で使用する場合、動作はシェルごとに異なりますが、通常は glob のために何もしません。のパターンは、次のファイルにのみ一致します。ディレクトリリストの名前。のすべての(非表示)ファイル名と一致します。おそらく、ここではglobが分割されておらず、各ディレクトリエントリ名を逆にチェックしてディレクトリエントリ名を含めることができないからです。これは実際になぜまだ含まれているのか説明しません。 sまたはsを含むファイル名、またはsを含むファイル名は除外されますが、sまたはsを含むファイル名は除外されません。bash -O extglobzsh -o kshglobbash!(*/)/*//!(*[a/b]*)ab!(*[a")"/b])a)b

型が指定されていないファイルが必要な場合目次シンボリックリンクを確認した後は、glob単独で実行できる操作ではなく、zsh実際には名前以外の属性に基づいてファイルを選択できるglob修飾子を使用する必要があります。

print -rC1 -- *(-^/)

ここで zsh は glob をマッチさせ、globbing の後に追加のステップとして修飾子を適用します。これは-、シンボリックリンクの確認後(stat()代わりにlstat())次の修飾子が適用され、^次の修飾子が無効になり、/ファイルタイプが選択されることを指定します。目次

4.4+では、常にNULで区切られた結果を印刷し、それを使用して結果を取得するbash他のツールにタスクをアウトソーシングできます。たとえば、次のようになります。readarray -td ''

readarray -td '' files < <(zsh -c 'print -rNC1 -- *(N^-.)')
(( ${#files[@]} )) && ls -Fd -- "${files[@]}"

またはGNUを使用findして、次のようにしますsort

readarray -td '' files < <(
  LC_ALL=C find . -mindepth 1 -maxdepth 1 \
    ! -name '.*' ! -xtype d -printf '%P\0' | sort -z)
(( ${#files[@]} )) && ls -Fd -- "${files[@]}"

sort(これは、同じリストを得るためにリストをソートしzshますが、そのリストをに渡す特別な場合は、セルフソートと同じように重複します。lsls

NULで区切られたリストがある場合は、配列ステップをスキップして出力をに渡すこともできますxargs -r0 ls -Fd --。これにより、空のリストケースを特別に処理して解決する必要がなくなります。引数のリストが長すぎます。限定。


1 ただし、パスをフィルタリングして全体に一致させるために、グローバル glob 以降の追加手順として適用できる拡張~glob 演算子も参照してください。では、globのファイル名生成アルゴリズムを実行し、パターンを使用して結果のパス名をフィルタリングします。zsh/a*/b*/c*~*e*a*/b*/c**e*

²大文字と小文字を区別しないワイルドカードを使用すると、次のように変更できます。zsh -o nocaseglob

関連情報