grep 具有类似 Bash-Globbing 的搜索模式

grep 具有类似 Bash-Globbing 的搜索模式

我想使用一个非常简单的模式与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 所说,你需要引用该模式


还有一些,仅供娱乐

  • function mygrep
      while read --line line
        string match -- $argv[1] $line
      end
    end
    
  • #!/usr/bin/env tclsh
    proc glob_grep {argv} {
        lassign $argv pattern
        while {[gets stdin line] != -1} {
            if {[string match $pattern $line]} {
                puts $line
            }
        }
    }
    glob_grep $argv
    

答案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"

您必须考虑如何处理已经具有转义的字符串(例如,\?可能不应该成为\.\*不应该成为\.*),但是如果不确切知道您想要支持什么语法,那么不值得为此投入更多精力。

相关内容