3

I am creating this function to make multiple grep's over every line of a file. I run it as following:

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;
}

The idea is to print every line that contains all the strings: string1, string2, ..., stringN. This already works but I want to avoid the usage of the for to construct the expression:

| grep string1 | grep string2 ... | stringN

And if it's possible, also the usage of eval. I tried to make some expansion as follows:

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

And I get:

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

This is almost what I want but the problem is that when I try:

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

The expansion doesn't occur because bash cant expand {1..$#} due to the $#. It just works with numbers. I would like to construct some expansion that works in order to avoid the usage of the for in the agrep function.

4 Answers 4

5
agrep () {
    if [ $# = 0 ]; then
        cat
    else
        pattern="$1"
        shift
        grep -e "$pattern" | agrep "$@"
    fi
}
Sign up to request clarification or add additional context in comments.

3 Comments

thanks for the support this was very useful, but i have one question, the if statement is optional or is necessary, it's unclear for me that if the if is true then execute cat, i am not sure about the usage of that.
In the case of no arguments given at all, or after all of the arguments have been peeled off, agrep needs to pass its input through to its output unmodified. The cat command does that.
ok thanks for the support an explanation, this was very useful.
3

Instead of running each multiple greps on each line, just get all the lines that match string1, then pipe that to grep for string2, etc. One way to do this is make agrep recursive.

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

It's not the most efficient solution, but it's simple.

(Be sure to note Rob Mayoff's answer, which is the POSIX-compliant version of this.)

Comments

2

awk to the rescue!

you can avoid multiple grep calls and constructing the command by switching to awk

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

enter your space delimited strings as in the example above.

3 Comments

Depending on the patterns to match, it may be tricky to find a way to pack them all into a single delimited string. (What if one of the strings itself contains whitespace, for example?)
it doesn't have to be space delimited (mimicking original example) you can pick any unused char as a delimiter but need to split with that too.
In general, you can't guarantee there is an unused character, or at least know ahead of time what is unused. It's the same problem arrays were introduced to solve.
2

Not building a string for the command is definitely better (see chepner's and Rob Mayoff's answers). However, just as an example, you can avoid the for by using printf:

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

Using printf also helps somewhat with special characters in the patterns. From 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)

Since the aim of %q is providing output suitable for shell input, this should be safe.

Also: You almost always want to use "$@" with the quotes, not just plain $@.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.