grep 的多个 -e 选项

grep 的多个 -e 选项

我想将多个模式传递给grep,并且有许多关于如何将-e选项应用于每个模式的解决方案。

不同的模式存储在数组中ptrn。我想知道数组元素中的空白是否会被误解,因为它们-e不会单独传递给模式。

可能性是

可能性1

mptrn=$( printf -- ' -e %s' "${ptrn[@]}" )
grep -E "$mptrn" -- "$flnm"

可能性2

for i in "${!ptrn[@]}"; do
  ptrn[$i]="-e ${ptrn[$i]}"
done
grep -E "${ptrn[@]}" -- "$flnm"

可能性3

eptrn=()
for i in "${!ptrn[@]}"; do
  eptrn+=("-e" "${ptrn[$i]}")
done
grep -E "${eptrn[@]}" -- "$flnm"

任何解决方案可能存在哪些问题?

答案1

这么说吧ptrn=(" a b" " 333 22 1 ")

答案1:

mptrn=$( printf -- ' -e %s' "${ptrn[@]}" )
grep -E "$mptrn" -- "$flnm"

该命令的整个输出将被分配给mptrn,并且如果数组中的元素包含空格字符,则使用的格式将不允许区分它们。mptrn将包含-e a b -e 333 22 1(带有前导和尾随空格字符)。执行时grep,由于前导空格,带引号的参数将被解释为模式参数,它将搜索给"$mptrn"定文件中出现的 ,这不是您想要实现的目标。要使类似的方法发挥作用,您可以使用IFS范围。

[ "${IFS+x}" = x ] && OLD_IFS=$IFS
IFS=$(printf '\x7f')
mptrn=$(printf "-e${IFS}%s${IFS}" "${ptrn[@]}")
# note that we don't use quotes
grep -E $mptrn -- "$flnm"
if [ "${OLD_IFS+x}" = x ]; then IFS=$OLD_IFS; else unset IFS; fi

答案2:

for i in "${!ptrn[@]}"; do
  ptrn[$i]="-e ${ptrn[$i]}"
done
grep -E "${ptrn[@]}" -- "$flnm"

这个答案接近于期望的结果,除了搜索的模式将包含前导空格,这意味着生成的命令如下所示:

grep -E "-e  a b" "-e  333 22 1 " -- file

只需删除空格即可解决此问题:ptrn[$i]=-e${ptrn[$i]}


答案3:

eptrn=()
for i in "${!ptrn[@]}"; do
  eptrn+=("-e" "${ptrn[$i]}")
done
grep -E "${eptrn[@]}" -- "$flnm"

这个答案是正确的,生成的grep命令将是:

grep -E -e " a b" -e " 333 22 1 " -- file

答案2

将它们组合成一个正则表达式。例如,如果您想匹配任何模式,您可以使用正则表达式交替运算符|作为 OR。

首先,join_by在 bash 中创建一个函数:

join_by () { 
    local d="$1";
    shift;
    printf '%s' "$1";
    shift;
    printf '%s' "${@/#/$d}"
}

perl这是的内置函数的相当简单的克隆join。它使用第一个参数作为分隔符,然后使用该分隔符连接所有剩余的参数,而无需额外烦人的前导或尾随分隔符。

然后像这样使用它:

$ p=(a b c d e)
$ re=$(join_by '|' "${p[@]}")
$ echo $re
a|b|c|d|e

您可以使用 $re grep -E(替换需要扩展正则表达式,“ERE”)。

grep -E "$re" filename

如果您需要执行更复杂的布尔运算(例如a && b && ! (c || d || e),请使用 perl 或 awk。 bash 是数据处理语言的糟糕选择。

相关内容