如何避免在此bash函数中使用“for”循环?

时间:2016-04-03 02:03:58

标签: bash

我正在创建这个函数,在文件的每一行上创建多个grep。我运行如下:

cat file.txt | agrep string1 string2 ... stringN 

function agrep () {  
   for a in $@;  do
     cmd+=" | grep '$a'";
   done ;
   while read line ; do
     eval "echo "\'"$line"\'" $cmd";
   done;
}

我们的想法是打印包含所有字符串的每一行:string1string2,...,stringN。这已经有效,但我想避免使用for来构造表达式:

| grep string1 | grep string2 ... | stringN

如果可能,也使用eval。我尝试进行如下扩展:

echo "| grep $"{1..3}

我得到了:

| grep $1 | grep $2 | grep $3

这几乎是我想要的,但问题是当我尝试时:

echo "| grep $"{1..$#}

由于{1..$#}由于bash无法扩展$#,因此无法进行扩展。它只适用于数字。我想构建一些有效的扩展,以避免在for函数中使用agrep

4 个答案:

答案 0 :(得分:5)

agrep () {
    if [ $# = 0 ]; then
        cat
    else
        pattern="$1"
        shift
        grep -e "$pattern" | agrep "$@"
    fi
}

答案 1 :(得分:3)

不是在每一行上运行每个多个grep,而是获取与string1匹配的所有行,然后将其grep传递给string2,等等。这样做的方法是使agrep递归。

agrep () {
    if (( $# == 0 )); then
        cat   # With no arguments, just output everything
    else
        grep "$1" | agrep "${@:2}"
    fi
}

它不是最有效的解决方案,但它很简单。

(请务必注意Rob Mayoff's answer,这是符合POSIX标准的版本。)

答案 2 :(得分:2)

awk救援!

你可以通过切换到awk来避免多个grep调用和构造命令

awk -v pat='string1 string2 string3' 'BEGIN{n=split(pat,p)} 
               {for(i=1;i<=n;i++) if($0!~p[i]) next}1 ' file

输入您的空格分隔字符串,如上例所示。

答案 3 :(得分:2)

为命令构建字符串肯定更好(请参阅chepner'sRob Mayoff's答案)。但是,举个例子,您可以使用for

来避开printf
agrep () { 
    cmd=$(printf ' | grep %q' "$@")
    sh -c "cat $cmd"
}

使用printf也有助于模式中的特殊字符。来自help printf

In addition to the standard format specifications described in printf(1),
printf interprets:

  %b    expand backslash escape sequences in the corresponding argument
  %q    quote the argument in a way that can be reused as shell input
  %(fmt)T output the date-time string resulting from using FMT as a format
        string for strftime(3)

由于%q的目标是提供适合shell输入的输出,这应该是安全的。

另外:您几乎总是希望"$@"使用引号,而不仅仅是$@