web-dev-qa-db-ja.com

シェルグロビングのゼロ以上の一致演算子

私はここでかなり些細な問題に悩まされています:* bash内の記号は、sedなどのツールと同様に、ゼロ以上を意味します。

例えば、 ak*は、名前が完全にaの後に0個以上のksが続くファイルと一致する必要があります。その拡張には、aakakk、およびakkkが含まれますが、akcは含まれません。

私はすでに試しましたunsetopt sh_glob zshおよびset -o noglob bashで;彼らは望ましい行動を生み出しませんでした。

9
Kira

_ksh93_ を除き、通常のシェルには、sed、awkなどと同じ構文の正規表現がありません。これらは、ファイルの照合に使用できます。

Ksh93、bash、zshには、グロブと下位互換性のある異なる構文の正規表現があります。

  • _?_は任意の1文字に一致します(通常の正規表現構文の_._など)
  • _[…]_は、ほとんど同じ方法で文字セットに一致します
  • *(FOO)は、任意の数の_FOO_と一致します(通常の正規表現構文の_(FOO)*_と同様)
  • 同様に、+(FOO)は1回以上の出現に一致し、?(FOO)は0回または1回の出現に一致します
  • @(FOO|BAR)は、_FOO_または_BAR_に一致します
  • 一致は部分文字列ではなく文字列全体に適用されます。部分文字列が必要な場合は、最初と最後に_*_を置きます

この構文は、bashでは_shopt -s extglob_、zshでは_setopt ksh_glob_を使用してアクティブにする必要があります。だからbashではあなたは書くでしょう

_shopt -s extglob
ls a*(k)
_

参照 なぜ私の正規表現はXでは機能するがYでは機能しないのですか?

Ksh93、zsh、およびbashは、_=~_構成の_[[ … ]]_演算子を使用して、文字列に対して拡張正規表現(基本的にawkの構文)との正規表現マッチングを実行できます。ただし、これはファイルの一覧表示には便利ではありませんが、本当に必要な場合は実行できます。

_shopt -s dotglob  # <<< include dot files, for bash
setopt globdots   # <<< include dot files, for zsh
FIGNORE='@(.|..)' # <<< include dot files, for ksh
for x in *; do
  if [[ $x =~ ^ak*$ ]]; then
    …
  fi
done
_

ls ak{k,}は、akで始まり、その後に別のkが続くか何もないファイルを表示します。

$ touch ak akk akc
$ ls -l ak{k,}
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 ak
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 akk

グロブは正規表現ではありませんが、*?だけではありません。

正規表現を使用して一致するファイル名を検索する場合は、findコマンドを使用できます。

$ find . -maxdepth 1 -type f -regex './ak+$' 
./ak
./akk

-maxdepth 1オプションは、検索を現在のディレクトリのみに制限します(サブディレクトリは検索されません)

大文字と小文字を区別しない検索が必要な場合は、-iregexではなく-regexを使用してください。

他のコマンドでfindが見つけたファイルを使用する方法は多数あります。例えば:

find . -maxdepth 1 -type f -regex './ak+$' -ls
find . -maxdepth 1 -type f -regex './ak+$' -exec ls -ld {} +
find . -maxdepth 1 -type f -regex './ak+$' -print0 | xargs -0r ls -ld
ls -ld $(find . -maxdepth 1 -type f -regex './ak+$')

最後の例は、1。ファイル名の空白などに対応していない、2。コマンドラインの長さの制限など、さまざまな障害モードが発生しやすい傾向があります。推奨されません。

4
cas

シェルに応じたいくつかのオプション:

$ touch a akk aka
$ ksh -c 'echo a*(k)'
a akk
$ zsh -o kshglob -o nobareglobqual -c 'echo a*(k)'
a akk

nobareglobqualその末尾の(k)は、ここではグロブ修飾子として解釈されません)

$ bash -O extglob -c 'echo a*(k)'
a akk

$ zsh -o extendedglob -c 'echo ak#'
a akk

zsh#は、正規表現*と同等です。

ksh93は、グロブでいくつかのタイプの正規表現を使用することもできます。

$ ksh93 -c 'echo ~(E:ak*)' # extended RE
a akk
$ ksh93 -c 'echo ~(P:ak*)' # Perl-like RE
a akk
$ ksh93 -c 'echo ~(X:ak*)' # AT&T augmented RE
a akk
$ ksh93 -c 'echo @(~(E)ak*)' # alternative syntax
a akk
2

bashで使用できる構文は次のとおりです。ls a+(k)これは、有効になっているbashshoptシェルオプションextglobに依存します。 Ubuntu 14.04 GNU/Linuxでは、これはデフォルトで有効になっているようです。

これがどのように機能するかです:

$ shopt extglob
extglob         on
$ ls
ak  akc  akd  akk  akkk  akkkk
$ ls a+(k)
ak  akk  akkk  akkkk
$ shopt -u extglob
$ shopt extglob
extglob         off
$ ls a+(k)
bash: syntax error near unexpected token `('
$

Bashマニュアルから:

+(パターンリスト)

指定されたパターンの1回以上の出現に一致します。

パターンリストは、「|」で区切られた1つ以上のパターンのリストです。

https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching のBashマニュアルでこれを参照してください。

2
RobertL