222

As a part of this script, I need to be able to check if the first argument given matches the first word of file. If it does, exit with an error message; if it doesn't, append the arguments to the file. I understand how to write the if statement, but not how to use grep within a script. I understand that grep will look something like this

grep ^$1 schemas.txt

I feel like this should be much easier than I am making it.

I'm getting an error "too many arguments" on the if statement. I got rid of the space between grep -q and then got an error binary operator expected.

if [ grep -q ^$1 schemas.txt ]
then
        echo "Schema already exists. Please try again"
        exit 1
else
        echo "$@" >> schemas.txt
fi
3
  • 1
    Lose the [] and it'll work. Though you probably want to quote your pattern: if grep -q "^$1" schemas.txt; then …
    – derobert
    Sep 18, 2012 at 19:25
  • 1
    one line solution using Bash's "Group Command" feature: stackoverflow.com/questions/6550484/… Sep 27, 2016 at 19:46
  • 1
    Bash oneliner. If the string "foo" is found in file content.txt then run the command work.sh, otherwise do nothing : cat content.txt | grep "foo" && work.sh Nov 18, 2020 at 16:07

6 Answers 6

318

grep returns a different exit code if it found something (zero) vs. if it hasn't found anything (non-zero). In an if statement, a zero exit code is mapped to "true" and a non-zero exit code is mapped to false. In addition, grep has a -q argument to not output the matched text (but only return the exit status code)

So, you can use grep like this:

if grep -q PATTERN file.txt; then
    echo found
else
    echo not found
fi

As a quick note, when you do something like if [ -z "$var" ]…, it turns out that [ is actually a command you're running, just like grep. On my system, it's /usr/bin/[. (Well, technically, your shell probably has it built-in, but that's an optimization. It behaves as if it were a command). It works the same way, [ returns a zero exit code for true, a non-zero exit code for false. (test is the same thing as [, except for the closing ])

11
  • 1
    Why doesn't the if statement need the brackets? I have it working without, but don't understand why. Can I still nest it without the brackets?
    – Lauren
    Sep 18, 2012 at 18:47
  • @Lauren did you miss the quick note? [ is not part of the if syntax, it's (conceptually) a command you're running, just like grep
    – derobert
    Sep 18, 2012 at 19:06
  • 3
    @Lauren You don't use grep inside of [, you use one or the other, depending on what condition you want to check. (You can use any command inside an if, btw, if just checks exit code.) … well, I guess you probably could come up with a reason to use grep inside [, but that'd be a fairly complicated script, and its not a normal thing to do.
    – derobert
    Sep 18, 2012 at 19:21
  • 3
    FYI, run ls -l /usr/bin/\[ and man [ to see that [ is a program like any other, it just looks like a syntactic element - this should make it obvious and easier to understand. (for convenience, [ is also a built-in in bash, dash, and others - but it's still a command). also try type -all [ in bash.
    – cas
    Sep 18, 2012 at 22:56
  • 1
    @AlexanderCska if you want grep to print the matched lines (e.g, to pipe them somewhere), then omit the -q, that option tells grep to be quiet (not print the matched lines).
    – derobert
    Jul 22, 2019 at 16:00
76

Another simple way is to use grep -c.

That outputs (not return as exit code), the number of lines that match the pattern, so 0 if there's no match or 1 or more if there's a match.

So, if you wanted to check that the pattern is matched 3 or more times, you would do:

if [ "$(grep -c "^$1" schemas.txt)" -ge 3 ]; then
  ...
4
  • 27
    More precisely, it will output 1 if found only once. To output 1 regardless the amount of found matches use grep -cim1 instead.
    – manatwork
    Apr 29, 2013 at 10:26
  • This method can also be used to distinguish between grep error and grep 'pattern found 0 times'. (although not with the exact if statement used, i think a variable is required)
    – Evan Benn
    Apr 3, 2019 at 5:27
  • 1
    Explanation for grep -cim1: -c means print count, -i means case-insensitive (so remove this option if you don't want that), and -m means stop reading after this many lines match. So it effectively means grep --count --ignore-case --max-count=1.
    – Sumit
    Oct 6, 2020 at 4:29
  • this worked better for me than the accepted solution (which didn't work at all )
    – m1m1k
    Feb 16 at 16:51
16

I know I am late for this, but I love this short version:

grep -q ^$1 schemas.txt && echo "Schema already exists. Please try again" || echo "$@" >> schemas.txt
1

If we want to catch the first word of a file we need do add -zw to grep

if grep -qzw "^$1" file
then 
   ... 
else 
   ... 
fi

Without -z we get the first word of a line. Without -w we get partial words.

0

Old post but really the answer was never given.
Yes Grep returns 0 if pattern is found (true) and 1 if the pattern is not found (false). Keep in mind grep is line based so the search term you thought would do it all might give you a lot of garbage hits, I leave this up to you to figure out how to parse. $? is the exit status, I prefer to use pipes rather than reading from a file as it suits my scripts better. I use the following:

{cat/cut/echo/curl/whatever} | grep -q PATTERN |echo $?

If the above returns 0 your pattern was found, if it returns 1 it was not.

This is useful as I place it in while loops to monitor my processes and keep certain scripts alive if they die or notify me if they have completed.

-2

If you want to use it with square brackets, you can execute the below

if [ `grep -q PATTERN file.txt` ]; then
    echo found
else
    echo not found

This Logic works for all commands, Just place your commands inside backtick (the button above tab or to the down of Esc button or to the left of 1 button)

3
  • 1
    You'd need to quote the `grep -q PATTERN file.txt`, otherwise the split+glob operator is applied and that would cause problem for any output that contains characters of $IFS or wildcard characters. More generally, [ "`cmd`" ] would return true if cmd outputs at least one non-empty line on stdout (with potential problems if it outputs NUL bytes). That means the shell will have to store the whole output in memory and wait for it to finish. Using if cmd | grep -q . instead may be preferable in that regard. Aug 24, 2016 at 13:01
  • 3
    This command would make no sense at all. The purpose of the -q switch is to suppress grep's output. You are saying: Check for PATTERN in file.txt, suppress any output, then set the return status according to whether PATTERN was found. Then, ignore the return status, take the command output (which we forced to be empty), apply word splitting and file glob expansion (resulting in no arguments at all), then run the [ (a.k.a. test) command with exactly one argument--namely, ]--and then, since that will result in an error, echo "not found". @StéphaneChazelas, did I miss something?
    – Wildcard
    Sep 16, 2016 at 22:51
  • 2
    @Wildcard, you're right, I apparently overlooked the -q. That comment would have made sense for [ `grep PATTERN file.txt ` ], but as hinted in the comment, [ "`grep -n PATTERN file.txt`" ] would be better if PATTERN may match only empty lines. Though here of course it's if grep -q PATTERN file.txt you want. Sep 17, 2016 at 6:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.