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?
-
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).Charles Duffy– Charles Duffy2016-03-30 15:49:24 +00:00Commented Mar 30, 2016 at 15:49
5 Answers
Testing $? is an anti-pattern.
if ./somecommand | grep -q 'string'; then
echo "matched"
fi
4 Comments
grep -Fxq F stands for fixed (not interpreted) and x for the whole line$? is anti-pattern?$? 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.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
= to be a comparison operator, not ==; see pubs.opengroup.org/onlinepubs/9699919799/utilities/test.htmlgrep '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).matched?A clean if/else conditional shell script:
if ./somecommand | grep -q 'some string'
then
echo "exists"
else
echo "doesn't exist"
fi
1 Comment
else addition.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.