我想使用一个非常简单的模式与grep
类似命令匹配。背景是 grep 虚拟机名称列表,例如仅列出虚拟机名称的命令,我在其中查找名称以“.local”结尾的虚拟机
正确的是:
# virsh list --name | grep '\.local$'
我想用类似的东西
# virsh list --name | mygrep *.local
不需要字符类或范围表达式,但应该应用 shell 通配符的基本模式:
*
零个或多个字符?
仅针对一个角色- 字符串的开始或结束没有额外的标记,这是由模式的开始和结束暗示的
- 点没有特殊含义
.
(为简单起见,请忽略 bash 首先对文件名进行通配)
答案1
作为一场狂欢功能
mygrep() {
local pattern=$1 line
while IFS= read -r line; do
if [[ $line == $pattern ]]; then
printf '%s\n' "$line"
fi
done
}
==
其中的运算符[[...]]
是模式匹配操作员。
参考:https://www.gnu.org/software/bash/manual/bash.html#index-_005b_005b
但是,正如 @Panki 所说,你需要引用该模式
还有一些,仅供娱乐
答案2
通过 ast-open 的实现grep
(也是作为 ast-open 的一部分构建的 ifgrep
的内置函数ksh93
,您可以使用 启用它builtin grep
),您可以将您的定义mygrep
为:
mygrep() {
grep -xK "$@"
}
where-K
将正则表达式风格从默认的基本正则表达式切换为 ksh glob 模式(bash -O extglob
例如支持的模式的超集),并-x
打开精确匹配(模式必须匹配整行,而不是行的任何部分)。
无论如何,您需要将其调用为:
virsh list --name | mygrep '*.local'
为了防止 shell 将其解释*.local
为 glob,因此它会按字面意思传递给您的mygrep
函数。
如果使用zsh
,您可以执行以下操作:
alias mygrep='noglob grep -xK'
对于不在 的参数中扩展的 glob mygrep
,然后能够执行以下操作:
virsh list --name | mygrep *.local
但这意味着您不能执行以下操作:
mygrep *.local ./*.txt
在当前目录的文件*.local
中查找与 glob 模式匹配的行。.txt
在 zsh 中,您可以将数组元素过滤为与 glob 模式不匹配的元素${array:#pattern}
或与 glob 模式匹配的元素${(M)array:#pattern}
。您可以使用 将命令输出的非空行放入数组中${(f)"$(cmd)"}
,因此在这里您可以将两者结合起来:
pattern=*.local
print -rC1 -- ${(M)${(f)"$(virsh list --name)"}:#$~pattern}
或者作为一个函数:
cmdglobline() print -rC1 -- ${(M)${(f)"$("$@[2,-1]")"}:#$~1}
cmdglobline '*.local' virsh list --name
您可以启用该extendedglob
选项以获得更高级的全局模式运算符(例如不区分大小写的匹配、间隔重复、近似匹配、否定...)。
答案3
python
我很快用s做了一些东西fnmatch.filter
。
#!/usr/bin/env python3
import fnmatch
import sys
def main():
try:
pattern = sys.argv[1]
except IndexError:
print('mygrep: No pattern supplied', file=sys.stderr)
sys.exit(1)
results = fnmatch.filter([_.rstrip('\n') for _ in sys.stdin.readlines()], pattern)
for line in results:
print(line)
if __name__ == '__main__':
main()
将其放在您的某个位置PATH
,使其可执行。应该可以解决这个问题,尽管边缘有点粗糙。
正如评论中提到的,您还需要引用该模式以避免 shell 完成文件名。
virsh list --name | mygrep '*.local'
答案4
你说你想要“类似globbing”的语法,所以不清楚你想要支持什么语法,但要停用BRE元字符,你可以编写mygrep()
转义所有不需要的BRE元字符,更改*
为.*
,也许更改?
为.
,并告诉 grep 匹配整行来获取类似通配语法的一个定义:
$ mygrep() {
local re
re=$(sed 's/[.$]/[&]/g; s/?/./g; s/\*/.*/g; s/\^/\\^/g' <<<"$1")
shift
printf 'Using grep -x -- "%s"\n' "$re" >&2
grep -x -- "$re" "$@"
}
$ mygrep '*.local' file
Using grep -x -- ".*[.]local"
您必须考虑如何处理已经具有转义的字符串(例如,\?
可能不应该成为\.
和\*
不应该成为\.*
),但是如果不确切知道您想要支持什么语法,那么不值得为此投入更多精力。