如何根据运行时提供的模式构建 grep & 命令来匹配多个模式?

4 grep shell-script

我已将一些MS Word文档导出为纯文本,并使用此函数解析.txt当前目录中文件的内容:

mo1 () {
for i in *.txt; do
    echo "File: $i"
    grep -n -HC 1 "$@" "$i"
done
}
Run Code Online (Sandbox Code Playgroud)

如果我要寻找不止一种模式,我可以做mo1 | grep pattern2. 但是,如果我想做一些事情,其结果grep -E 'pattern1.*pattern2[.*...]...'取决于运行时提供给函数的模式数量,即mo1 pattern1 pattern2 [...]等,该怎么办?我可以看到@数组可以提供项目的数量,并且我可以在循环中构造一个变量 (finalpattern='$1.*$2.*$3'),该变量最终将成为用于grep. 但我想不出如何抽象出你在函数中制作表达式的那一点?或者有没有更好/更简单的方法来做这样的事情?

Gil*_*il' 5

您可以利用printf内置功能。

mo1 () {
  for file in *.txt; do
    grep -n -C1 "$(printf "%s.*" "$@")" "$file"
  done
}
Run Code Online (Sandbox Code Playgroud)

这个简单的版本.*在最后一个元素之后插入。对于这个特定用例并不重要,但在其他情况下(例如grep -o),您可能需要.*在最后去除多余的部分。

mo1 () {
  pattern=$(printf "%s.*" "$@")
  pattern=${pattern%??}
  for file in *.txt; do
    grep -n -C1 "$pattern" "$file"
  done
}
Run Code Online (Sandbox Code Playgroud)

在 bash 中,您可以将printf输出直接放在一个变量中,这比使用命令替换稍快(但这不太可能重要,即使在子 shell 很慢的 Cygwin 上)。

mo1 () {
  printf -v pattern "%s.*" "$@"
  pattern=${pattern%??}
  for file in *.txt; do
    grep -n -C1 "$pattern" "$file"
  done
}
Run Code Online (Sandbox Code Playgroud)

如果要在位置参数之间插入单个字符,可以设置IFS为该字符并使用"$@". 但如果分隔符不止一个字符,这将不起作用。在 ksh 和 bash 中,如果模式中没有出现的字符,您可以使用它进行连接,然后执行替换。例如,在这里,模式包含换行符是没有意义的,所以:

mo1 () {
  typeset IFS=$'\n'
  typeset pattern="$*"
  pattern=${pattern//$'\n'/.*}
  for file in *.txt; do
    grep -n -C1 "$pattern" "$file"
  done
}
Run Code Online (Sandbox Code Playgroud)

在zsh中,当然有直接的方法。

mo1 () {
  for file in *.txt; do
    grep -n -C1 ${(j:.*:)@} $file
  done
}
Run Code Online (Sandbox Code Playgroud)