Why Does If [ !$(Grep -Q) ] Not Work When If Grep -Q Does

Why does if [ !$(grep -q) ] not work when if grep -q does?

A number of things are wrong with that first snippet.

You don't want [ ... ] if you want to test the return code. Drop those.

[] is not part of the if syntax (as you can see from your second snippet).

[ is a shell built-in and binary on your system. It just exits with a return code. if ...; then tests the return code of ....

$() is command substitution. It replaces itself with the output from the command that was run.

So [ !$(grep ...) ] is actually evaluating [ !output_from_grep ] and [ word ] is interpreted as [ -n word ] which will be true whenever word is non-empty. Given that ! is never non-empty that will always be true.

Simply, as indicated by @thom in his comment (a bit obliquely), add the ! negation to your second snippet with a space between it and grep.

What is the point of grep -q

The exit status of grep doesn't necessarily indicate an error ; it indicates success or failure. grep defines success as matching 1 or more lines. Failure includes matching zero lines, or some other error that prevented matching from taking place in the first place.

-q is used when you don't care about which lines matched, only that some lines matched.

if grep -q foo file.txt; then
echo "file.txt contains foo"
else
echo "file.txt does not contain foo"
fi

Why did I get different answers when I changed grep to egrep in the latter half of each

In egrep (or, preferably, grep -E), the | is a metacharacter, whereas in plain grep it is a plain (non-meta) character.

The |F$ term in egrep looks for an empty string or F at the end of line; it finds an empty string on every line.

The same term in grep looks for a |F at the end of line. To look for that with egrep, you'd need to escape the metacharacter with a backslash: grep -E '\|F$' enrolments.

In short, the plain grep command understands Basic Regular Expressions (BRE). The egrep or 'extended grep' command understands Extended Regular Expressions (ERE). Some versions of grep (such as GNU grep) can be compiled to recognize Perl-Compatible Regular Expressions (PCRE).

Why doesn't if [ echo $foo | grep -q bar ] work?

Short Answer

For your immediate use case, you simply want:

if echo "$confirm" | grep -q y; then

...or its much more efficient equivalent (if your shell is bash):

if [[ $confirm = *y* ]]; then

...or its much more efficient equivalent (for any POSIX shell):

case $confirm in *y*) echo "Put your code for the yes branch here" ;; esac

Why was the original wrong?

[ is not part of if syntax: if simply takes a (potentially compound) command as its argument before the then. [ is different name for the test command, which runs checks on its arguments; however, if what you want to test is the exit status of grep -q, then the test command doesn't need to be invoked for this purpose at all.

If you put a | inside a [ command, that makes your compound command a pipeline, and starts a new simple command. Arguments after the | are thus no longer passed to [.

With your original code:

if [ echo $confirm | grep -q y ]; then

...this was running two commands, with a pipeline between them:

[ echo $confirm # first command
grep -q y ] # second command

Since [ requires that its last argument be ], it reported that that mandatory argument was missing; and since grep treats extra arguments as filenames to read, it complained that no file named ] could be found.

Also, [ "$foo" ] checks whether the contents of foo is nonempty. Since the output of grep -q is always empty, [ "$(echo "$confirm" | grep -q y)" ], while syntactically correct, would always evaluate to false, even while exit status of grep -q changes to indicate whether a match was found. ([ "$(echo "$confirm" | grep y)" ], by contrast, is an alternative that emits a correct result - using [ ] to test whether the output from grep is or is not empty -- but is much less efficient than the best-practice approaches).


Formal if syntax

From help if:

if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi

The if COMMANDS list is executed. If its exit status is zero, then the
then COMMANDS list is executed. Otherwise, each elif COMMANDS list is
executed in turn, and if its exit status is zero, the corresponding
then COMMANDS list is executed and the if command completes. Otherwise,
the else COMMANDS list is executed, if present. The exit status of the
entire construct is the exit status of the last command executed, or zero
if no condition tested true.

Notably, if takes a list of COMMANDS, and no [ is included in the syntax specification.

Grep ascending order of cards. Why does it work?

Your regex is not at all valid, so I don't understand why you say it works.

Plain grep does not understand | to mean alteration. You can add an -E option to specify ERE (traditionally, egrep) regex semantics, or with POSIX grep backslash the |; or you can specify multiple -e options. (See e.g. https://en.wikipedia.org/wiki/Regular_expression#Standards for some background about the various regex dialects in common use.)

grep -Ev "[KQJT].*[2-9A].* |[KQ].*[JT].* |[6-9].*[2-5A].* "
grep -v "[KQJT].*[2-9A].* \|[KQ].*[JT].* \|[6-9].*[2-5A].* "
grep -ve "[KQJT].*[2-9A].* " -e "[KQ].*[JT].* " -e "[6-9].*[2-5A].* "

Even with this fix, the regex is obviously insufficient for removing matches where e.g. 3 is followed by 2. The only way to make it cover all cases is to enumerate every possibility. (Disallow 1 followed by any higher number, 2 followed by any higher number, 3 followed by any higher number, etc.) An altogether better approach would be to use a scripting language of some sort, and basically just map the symbols to ones with the desired sort order, then check if the input is sorted.

If that is not an option, maybe try

grep -E '^(A.)*(2.)*(3.)*(4.)*(5.)*(6.)*(7.)*(8.)*(9.)*(T.)*(J.)*(Q.)*(K.)* '

which looks for zero or more aces, followed by zero or more twos, followed by zero or more threes, etc.

How to use grep in if statement in shell

There is very likely no command named [grep. Drop the [

if grep -q "<pr>$i</pr>" ./archiv; then ...

[ is not and has never been a part of the shell grammar. It is a command, just like echo or test or grep. The value returned by that command is used to determine whether or not to execute the clause of the if statement.

/usr/xpg4/bin/grep -q [^0-9] does not always work as expected

Your validity test function happens to be more complicated than it should be. E.g. why do you use a command substitution with print for ${#1}? Why don't you use ${#1} directly? Next, forking grep to test for a non-number is a slow and expensive operation. What about this equivalent function, 100% POSIX and blazingly fast:

is_valid_id () {
# Takes one argument, which is the ID being tested.

if test ${#1} -ne 10; then
return 1 # ID length not exactly 10.
fi
case $1 in
(*[!0-9]*) return 1;; # ID contains a non-digit.
(*) return 0;; # ID is exactly 10 digits.
esac
}

Or even more simple, if you don't mind repeating yourself:

is_valid_id () {
# Takes one argument, which is the ID being tested.
case $1 in
([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]) # 10 digits.
return 0;;
(*)
return 1;;
esac
}

This also avoids your unquoted use of a grep pattern, which is error-prone in the presence of one-character file names. Does this work better?

Why wont my bashrc script work?

You don't want cmd-substitution for your tests, try

if screen -ls | grep -q ".testSession (Detached)" ; then
screen -r testSession
elif screen -ls | grep -q "No Sockets found"; then
screen -S testSession
else
echo "There is already a session with the Primary ID attached"
fi

Recall that if evaluates any following cmd-group for its return value (up to the closing ; then). The [ is an alias for the test command, but as grep -q will return 0 if your string is found and something >0 for other cases, this is all you need to test your screen connections.

IHTH



Related Topics



Leave a reply



Submit