テキストファイルに次のテキストがあります
$ 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の数値範囲をどのように区別できますか?
grep 201806{19..21} test
シェルによって次のように展開されます。
grep 20180619 20180620 20180621 test
grep
は、20180619
、20180620
、およびtest
の3つのファイル内で20180621
を検索すると理解します。
次のように変更した場合:
grep -e201806{19..21} test
次に、それは次のように拡張されます。
grep -e20180619 -e20180620 -e20180621 test
これは、e
で検索するgrep
に3つのtest
xpressionsを与えます。
またはあなたがすることができます:
printf '%s\n' 201806{19..21} | grep -f - test
式を入力行数としてgrep
に渡す場合(一部の実装では、/dev/stdin
の代わりに-
が必要になる場合があります)。
具体的には、zsh
を使用して、次のようにすることもできます。
numbers=({19..21} 25 31)
grep -E "201801(${(j:|:)numbers})" test
(j:|:)
パラメーター拡張フラグを使用して配列要素を|
(拡張正規表現代替演算子)で結合するため、EREとして使用できます。
または、次の方法でその配列を正規表現スカラーに関連付けることができます。
$ typeset -T re numbers '|'
$ numbers=({19..21} 25 31)
$ echo $re
19|20|21|25|31
正規表現には通常、番号範囲のマッチング機能はありませんが、zsh
パターン(extendedglob
の機能は正規表現と機能的に同等です)は<x-y>
演算子(一連の10進数):
print -rl -- ${(M)${(f)"$(<test)"}:#*201806<19-21>*}
はい、grep
はデフォルトでfirst引数のみを正規表現として扱います。
この意味は
_grep {1..9} file
_
に拡大します
_grep 1 2 3 4 5 6 7 8 9 file
_
他のオペランドで一致する式として_1
_を指定してgrep
を呼び出し、これらの他のオペランドはファイル名であることが期待されます。
他のコマンド:
_grep 201806* test
_
これは、ファイル名のグロビングパターンとして_201806*
_に一致しようとします。現在のディレクトリに名前が_201806
_で始まるファイルがないため、zsh
シェルはパターンの展開に失敗し、エラーメッセージ_no matches found
_を表示します。
他のボーンのようなシェルでは、パターンがどのファイル名とも一致しなかった場合、パターンは展開されずに残り、grep
の正規表現として使用されていました。正規表現として使用した場合、式_201806*
_は、_20180
_の後に0個以上の_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
_および_$3
_が範囲内の必要な数値に設定されます。次に、変数re
は201806(%s)
に設定され、printf
は_%s
_を_|
_で区切られたこれらの数値に置き換えます。
grep
呼び出しは、正規表現として201806(19|20|21)
を使用します。
引用符で囲まれていない文字列は、コマンドが実行される前にシェルによって解釈されます。この場合、試行したコマンドは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
を必要な範囲と比較します
[〜#〜] posix [〜#〜]utilsを使用したシェル(bash
なし):
seq 20180618 20180624 | grep -f - test
numgrep
:
numgrep '/20180618..20180624/' < test