231

I'm writing a shell script, and I'm trying to check if the output of a command contains a certain string. I'm thinking I probably have to use grep, but how can I do it?

1
  • Does the command need to keep running after generating the output string you're looking for, or can it be immediately closed at that time? (Your two answers differ in terms of their semantics in this respect). Commented Mar 30, 2016 at 15:49

5 Answers 5

265

Testing $? is an anti-pattern.

if ./somecommand | grep -q 'string'; then
  echo "matched"
fi
Sign up to request clarification or add additional context in comments.

4 Comments

If by any chance you only want to test a fixed string, add F and x options: grep -Fxq F stands for fixed (not interpreted) and x for the whole line
Why testing of $? is anti-pattern?
@VitalyZdanevich I assume because it's not robust against concurrency.
@VitalyZdanevich, for one, testing $? doesn't set the preceding commands as "checked" for purposes of set -e or the ERR trap, so your program can exit in cases where you want it to simply return down the intentionally-false path later. For another, $? is volatile global state -- it's easy to throw away its value by accident. For example, if you add a line of logging like echo "Exit status is $?", the new value in $? becomes the exit status of echo.
186

Test the return value of grep:

./somecommand | grep 'string' &> /dev/null
if [ $? == 0 ]; then
   echo "matched"
fi

which is done idiomatically like so:

if ./somecommand | grep -q 'string'; then
   echo "matched"
fi

and also:

./somecommand | grep -q 'string' && echo 'matched'

8 Comments

This code doesn't work with all POSIX shells: The POSIX standard only requires = to be a comparison operator, not ==; see pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
Also, grep 'string' &>/dev/null is both non-POSIX compliant and much slower to execute (if string appears early in a long output stream) than grep -q string. [The caveat there is if you want to be sure somecommand keeps running even after emitting string, in which case using grep -q -- by closing its stdin and exiting after the first instance of string is seen -- can be counterproductive]. (Re: "non-POSIX-compliant", &> is an extension -- see pubs.opengroup.org/onlinepubs/009695399/utilities/… describing the POSIX-mandated redirection support).
Would help if it was explained why that works/what each parameter does, to encourage full understanding of the syntax
can it print the result instead of matched?
|
19

Another option is to check for regular expression match on the command output.

For example:

[[ "$(./somecommand)" =~ "sub string" ]] && echo "Output includes 'sub string'"

Comments

18

A clean if/else conditional shell script:

if ./somecommand | grep -q 'some string'
then
  echo "exists"
else
  echo "doesn't exist"
fi

1 Comment

This seems to be a restatement of earlier answers, except the trivially obvious else addition.
10

Short answer

All the previous (very excellent) answers all assume that grep can "see" the output of the command, which isn't always true:

Success can be sent to standard output while failure to standard error.

So depending on which direction you test, your grep can fail. That's to say that if you are testing for the case of failure you must redirect the output of the command to standard output using 2>&1 in such a case as this.

Longer answer with proofs

I had what I thought was a very simple test in a bash script using grep and it kept failing. Much head scratching followed. Use of set -x in my script revealed that the variable was empty! So I created the following test to understand how things were breaking.

Note: iscsiadm is a Linux tool from the "open-iscsi" package used to connect/disconnect a host to SAN storage. The command iscsiadm -m session is used to show if any LUN connections are established):

#!/bin/bash

set -x

TEST1=$(iscsiadm -m session)
TEST2=$(iscsiadm -m session 2>&1)
echo
echo 'Print TEST1'
echo $TEST1
echo
echo 'Print TEST2'
echo $TEST2
echo

If a LUN was connected, both variables were successfully populated with values:

Print TEST1
tcp: [25] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:ipdisk.Target-LUN1 (non-flash) tcp: [26] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:storagehost.Target-LUN1 (non-flash)

Print TEST2
tcp: [25] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:ipdisk.Target-LUN1 (non-flash) tcp: [26] 192.168.X.XX:3260,1 iqn.2000-01.com.synology:storagehost.Target-LUN1 (non-flash)

However, if a LUN wasn't connected, iscsiadm sent the output to standard error, and only the "TEST2" variable was populated where we had redirected to standard output using 2>&1; "TEST1" variable which had no redirection to standard output was empty:

iscsiadm: No active sessions.

Print TEST1


Print TEST2
iscsiadm: No active sessions.

Conclusion

If you have a funky, half-broken—works in one direction but not the other—situation such as this, try the above test replacing iscsiadm with your own command and you should get the proper visibility to rewrite your test to work correctly.

2 Comments

That was the missing piece I needed. Commands like resize2fs need it (2>&1). Thanks for clarifying!
@FalloutBoy No prob; happy to help. This site has saved me tons of wasted cycles, so it seems right we all take a moment to contribute something back-

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.