テキストファイルのgrep番号範囲

テキストファイルのgrep番号範囲

テキストファイルに次のテキストがあります

$ cat test
20180618:
20180619:
20180620:
20180621:
20180622:
20180623:
20180624:

grepを使用して、次のように数値の範囲を見つけようとします。

$ grep 201806{19..21} test
grep: 20180619: No such file or directory
grep: 20180620: No such file or directory
grep: 20180621: No such file or directory

ZSHとbashの両方で上記のエラーが発生します。 grepが文字列をファイルとして検索しているようです。

私は別の方法を試しました。

$ grep 201806* test       
zsh: no matches found: 201806*

ZSHでのみこのエラーが発生します。 ZSHでこれを使用する正しい方法は何であり*、数値範囲に対してgrepにgrepを指示するにはどうすればよいですか?

答え1

ええ、grepそれを治療してください。最初デフォルトでは、パラメータは正規表現として使用されます。

これは意味する

grep {1..9} file

次に展開

grep 1 2 3 4 5 6 7 8 9 file

grepwithは1他のオペランドと一致する式で呼び出され、これらの他のオペランドはファイル名になると予想されます。

その他のコマンド:

grep 201806* test

これは201806*ファイル名のワイルドカードパターンで一致しようとします。201806現在のディレクトリに次の名前で始まるファイルがないため、zshシェルはパターンを拡張できず、エラーメッセージを表示しますno matches found

Bourneなどの他のシェルでは、パターンがファイル名と一致しない場合は拡張されずに使用されますgrep。式が201806*正規表現として扱われると、一致の20180後にゼロ個以上の6文字が続きます2018066666

代わりに、範囲に一致する正規表現を設定できます。

grep -E '201806(19|20|21)' test

または

grep -E '201806(19|2[01])' test

式の(シフト)を理解する必要があります-E(この交互にすると拡張正規表現になります)grep|


中かっこ拡張を使用して正規表現を構成することもできます。

set -- {19..21}
re=$( IFS='|'; printf '201806(%s)' "$*" )

grep -E "$re" test

これにより、まず位置パラメータと値が範囲内の目的の数値$1に設定されます。これにより、変数はで区切られた数字で置き換えられる位置に設定されます。$2$3re201806(%s)printf%s|

この呼び出しは正規表現grepとして使用されます。201806(19|20|21)

答え2

grep 201806{19..21} test

シェルを介して次に拡張されました。

grep 20180619 20180620 20180621 test

これは3つのファイルをgrep見つけることが理解できます。201806192018062020180621test

次のように変更すると:

grep -e201806{19..21} test

次に、次に展開します。

grep -e20180619 -e20180620 -e20180621 test

で検索できる3つの表現がありますegreptest

またはこれを行うこともできます:

printf '%s\n' 201806{19..21} | grep -f - test

式を複数行入力として渡します(一部の実装ではthisで置き換えるgrep必要があるかもしれません)。/dev/stdin-

具体的には、zsh次のこともできます。

numbers=({19..21} 25 31)
grep -E "201801(${(j:|:)numbers})" test

EREとして使用できるように、パラメータ(j:|:)拡張フラグを使用して配列要素を(拡張正規表現代替演算子)に関連付けます。|

または、次を使用して配列を正規表現スカラーにバインドすることもできます。

$ typeset -T re numbers '|'
$ numbers=({19..21} 25 31)
$ echo $re
19|20|21|25|31

正規表現には通常、数値範囲一致機能はありませんが、パターンzshextendedglob機能的に正規表現と同じ)は<x-y>演算子を使用できます(10進数列のみ)。

print -rl -- ${(M)${(f)"$(<test)"}:#*201806<19-21>*}

答え3

引用符のない文字列は、コマンドを実行する前にシェルによって解釈されます。あなたの場合、試しているコマンドは次に拡張されます。grep 20180619 20180620 20180621 test

$ echo grep 201806{19..21} test
grep 20180619 20180620 20180621 test

1つの回避策は、正規表現の置換を指定することです。

$ grep -E '201806(19|20|21)' test
20180619:
20180620:
20180621:

正規表現を使用して数値範囲を設定できますが、簡単ではありません。バラよりhttps://www.regular-expressions.info/numericranges.html詳しくは


別のオプションは、次のものを使用することです。awk

$ awk -F: '$1>=20180619 && $1<=20180621' ip.txt
20180619:
20180620:
20180621:

ここでは、線を分割し、最初のフィールドを:目的の$1範囲と比較します。

答え4

  1. POSIXシェル(いいえbash)とユーティリティ:

    seq 20180618 20180624 | grep -f - test
    
  2. numgrep:

    numgrep '/20180618..20180624/' < test
    

関連情報