1

I'm trying to grep multiple arguments in shell. I put orders like ./script arg1 arg2.. argN And I want them to act egrep -i "arg1" mydata | egrep -i "arg2" | ... egrep -i "argN" | awk -f display.awk in order to match patterns in AND format. What's wrong in my process?

Is it even right to code like egrep -i "arg1" mydata | egrep -i "arg2" | ... egrep -i "argN" | awk -f display.awk to get multiple patterns in AND format??

if [ $# -eq 0 ]
then
    echo "Usage:phone searchfor [...searchfor]"
    echo "(You didn't tell me what you want to search for.)"
    exit 0
else
    for arg in $*
    do
    if [ $arg -eq $1 ]
    then
            egrep -i "arg" mydata |
    else
            egrep -i "arg" |
    fi
    done
    awk -f display.awk     
fi

If my data has 'happy sunny bunny', 'sleepy bunny', and 'happy sunny'

I want them to perform if I tried ./script happy sunny bunny then only 'happy sunny bunny' comes out.

and if i tried ./script bunny then 'happy sunny bunny' 'sleepy bunny' both coming out.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Yusun Lim
  • 13
  • 4
  • 1
    Is the order important? I.e., would you want `./script happy sunny bunny` to match `sunny happy bunny` and `bunny sunny happy` as well? – tink Apr 02 '19 at 02:16
  • And no, the data from the first if won't make it into the `egrep` in the else clause. – tink Apr 02 '19 at 02:17
  • This might be of interest to you: https://www.shellhacks.com/grep-or-grep-and-grep-not-match-multiple-patterns/ – kvantour Apr 02 '19 at 05:31
  • https://shellcheck.net flags several problems with this code, (with a #!/bin/bash header line). But I'm skeptical of the body of the `if` and `else` paths, i.e. `egrep -i "arg" mydata |`. .... What is that trailing `|` (pipe) char? It needs a command on its right hand side too ;-) . Good luck. – shellter Apr 02 '19 at 05:40

3 Answers3

4

The immediate fix is to move the pipe character to after the done.

Also, you should loop over "$@" to preserve the quoting of your arguments, and generally quote your variables.

if [ $# -eq 0 ]
then
    # print diagnostics to stderr
    echo "Usage: phone searchfor [...searchfor]" >&2
    echo "(You didn't tell me what you want to search for.)" >&2
    exit 0
fi
for arg in "$@"
do
    # Add missing dash before eq
    if [ "$arg " -eq "$1" ]
    then
        # Surely you want "$arg" here, not the static string "arg"?
        grep -E -i "$arg" mydata
    else
        grep -E -i "$arg"
    fi
done |
awk -f display.awk     

The overall logic still seems flawed; you will be grepping standard input for the first argument if there are more than two arguments. Perhaps you want to add an option to allow the user to specify an input file name, with - to specify standard input? And then all the regular arguments will be search strings, like the usage message suggests.

If indeed the intent is to loop over all the arguments to produce a logical AND, try this:

also () {
    local what
    what=$1
    shift
    if [ $# -gt 0 ]; then
        grep -E -i "$what" | also "$@"
    else
        grep -E -i "$what"
    fi
}

also "$@" <mydata | awk -f display.awk

... though a better implementation might be to build a simple Awk or sed script from the arguments:

script='1'
for arg in "$@"; do
    script="$script && tolower(\$0) ~ tolower(\"$arg\")"
done
awk "$script" | awk -f display.awk

This breaks down if the search phrases could contain regex specials, though (which of course is true for the grep -E version as well; but then you could easily switch to grep -F).

Merging the two Awk scripts into one should probably not be hard either, though without seeing display.awk, this is speculative.

tripleee
  • 175,061
  • 34
  • 275
  • 318
1

You can solve it recursively:

#! /bin/bash
if (( $# == 0)); then
  exec cat
else
  arg=$1; shift
  egrep "$arg" | "$0" "$@"
fi

The recursion ends, if the script is called with no arguments. In this case it behaves like cat. In your example you can put your awk there. If the script is called with one or more arguemnts, it calles egrep with the first argument ($1) and passes the remaining arguments ($@ after shift) to itself ($0).

Example:

$ ./recursive-egrep sys  < /etc/passwd
sys:x:3:3:sys:/dev:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
$ ./recursive-egrep sys no  < /etc/passwd
sys:x:3:3:sys:/dev:/usr/sbin/nologin
ceving
  • 21,900
  • 13
  • 104
  • 178
0

Use G from https://gitlab.com/ole.tange/tangetools/tree/master/G which does this (except for the awk part).

SYNOPSIS

G [[grep options] string] [[grep options] string] ...

DESCRIPTION

G is a shorthand of writing (search for single lines matching expressions):

grep --option string | grep --option2 string2

or with -g (search full files matching expressions):

find . -type f | xargs grep -l string1 | xargs grep -l string1
Ole Tange
  • 31,768
  • 5
  • 86
  • 104