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

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

我已经出口了一些微软Word将文档转换为纯文本,并使用此函数解析.txt当前目录中文件的内容:

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

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

答案1

您可以利用printf内置的。

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

这个简单的版本插入到.*最后一个元素之后。对于这个特定的用例来说,这并不重要,但在其他情况下(例如),您可能需要在最后grep -o删除额外的内容。.*

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

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

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

如果您想在位置参数之间插入单个字符,您可以设置IFS为该字符并使用"$@"。但如果分隔符超过一个字符,则这种方法不起作用。在ksh和bash中,如果有一个字符没有出现在模式中,您可以使用它来加入,然后执行替换。例如,在这里,模式包含换行符是没有意义的,因此:

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

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

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

答案2

或者,您可以使用--filegrep 选项:

-f FILE, --file=FILE
    Obtain patterns from FILE, one per line.  The empty file contains
    zero patterns, and therefore matches nothing.  (-f is specified by POSIX.)

相关内容