我在文本文件中有以下文本
$ 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
将调用grep
with1
作为表达式来匹配其他操作数,并且这些其他操作数预计是文件名。
你的另一个命令:
grep 201806* test
这将尝试201806*
作为文件名通配模式进行匹配。201806
当前目录中没有名称以 开头的文件,因此zsh
shell 无法扩展该模式并给出错误消息no matches found
。
在其他类似 Bourne 的 shell 中,如果模式未与任何文件名匹配,它将保持未展开状态并用作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
范围$3
内所需的数字。然后,该变量re
将被设置为将替换为由 分隔的这些数字的201806(%s)
位置。printf
%s
|
该grep
调用将用作201806(19|20|21)
正则表达式。
答案2
grep 201806{19..21} test
由 shell 扩展为:
grep 20180619 20180620 20180621 test
这可以理解为在 3 个文件中grep
查找、和。20180619
20180620
20180621
test
如果你把它改成:
grep -e201806{19..21} test
然后扩展到:
grep -e20180619 -e20180620 -e20180621 test
其中给出了在 中搜索的3 个e
表达式。grep
test
或者你可以这样做:
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>
运算符(仅适用于十进制数字序列):
print -rl -- ${(M)${(f)"$(<test)"}:#*201806<19-21>*}
答案3
不带引号的字符串在执行命令之前由 shell 解释,在您的情况下,您尝试的命令将扩展为grep 20180619 20180620 20180621 test
$ echo grep 201806{19..21} test
grep 20180619 20180620 20180621 test
一种解决方法是指定正则表达式替换:
$ 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
POSIXshell (no
bash
) 与 utils:seq 20180618 20180624 | grep -f - test
-
numgrep '/20180618..20180624/' < test