In Bash (and many other programming languages developed for or on the UNIX environment), the !=
operator means 'is not equal to'.
In mathematical notation you would write ≠
, but that character did not exist in ASCII, so most programming languages adopted an alternative. (APL just adopted its own character set instead, so it could go right ahead and use ≠
, thankyouverymuch.) Different languages chose differently; Ada and Erlang approximate the mathematical symbol with /=
(Erlang even has a =/=
version), while BASIC and Pascal use <>
. Fortran side-stepped the whole issue by using letters for all its relational operators, so "not equal" is .ne.
to go with .lt.
for "less than", .le.
for "less than or equal", .eq.
for "equal", and .ge.
and .gt.
for "greater than or equal" and "greater than".
But most UNIX languages borrow their syntax from C, in which "not equal" is spelled !=
. It's not obvious, but it is logical, since !
by itself means NOT in the same language. (! 0 is 1, ! 1 is 0).
So [ $? != 0 ]
is true if $?
is not 0. (What's $?
? See below.)
Note that this is doing a string comparison, which is probably not what you want here. If it were [ $? != 00 ]
, the condition would always be true because $? will never be the two-character string "00". Putting the expression inside ((
...))
makes it compare numerically instead: (( $? != 0 ))
is true only if the value of $?
, evaluated as a number, is not zero, and would mean the same thing if you wrote (( $? != 00 ))
. You could also stick with square brackets, but change operators; to do a numeric comparison inside those, you use something very like the Fortran spellings: [ $? -ne 0 ]
.
Anyway, what's the point of the test? Well, the special shell parameter $?
contains the exit code from the last command run. Every command you run from the shell reports a numeric status back to the shell when it finishes running; in general, a value of 0 means the command succeeded, and a nonzero value means it failed. (That seems backward, but the idea is that there's usually only one way to succeed but potentially lots of ways things can go wrong. There's likewise only one zero but lots of nonzero numbers, so a specific number can provide more information about what happened.)
The grep
command searches a file for lines matching a pattern. If it finds any matching lines, it prints them out, but it also exits with a 0 ("success") status if it found at least one match. If it is able to do the search successfully but doesn't find anything, it exits with status 1 instead.
So running if [ $? != 0 ]
right after a grep
is saying "Hey, if that grep didn't find anything, do this."
But that's an unnecessarily roundabout way of doing such a test. The thing is, what you pass to if
in bash is already a command to run; the then
block is executed if the command exits with status 0. So the [
...]
brackets are just another shell command, one that interprets the expression and exits with status 0 if it's true and 1 if it's false.
That means that this sequence:
somecommand
if (( $? == 0 )); then
...
fi
is just a long way of writing this equivalent sequence:
if somecommand; then
...
fi
Now in this case, we're checking for not zero instead of zero, but you can achieve that same result by using the !
by itself, which is "not", just like it is in C. When it's the start of a command line, it flips the sense of the exit code returned by the command:
if ! somecommand; then
...
fi
So the code in the OP could be written more succinctly like this:
if ! grep -i "$name" ~uli101/uli101/phonebook; then
...
fi
(Note the double-quotes around $name
; you should almost always surround parameter expansions with double-quotes, as that stops the shell from doing things like word re-splitting. In this case, without quotes, a space in $name
would turn its value into multiple arguments to grep
instead of just one; the pattern it looked for would stop at the space, and the part after that would be interpreted as the name(s) of the first file(s) to look in.)