[...]
is not a structure operator. It is a command. Like grep is. Well, it is nowadays a built-in command, but yet, still a command. Back in the time, [
was litteraly a command. A unix program. That you could find in /usr/bin/[
(it is still there by the way. ls it on your PC. but not for bash, which has its own builtin [
). In fact, it was then a link to /usr/bin/test
. A command whose purpose is to test things.
It takes some arguments. Does some computation. And has an exit code 0 or 1, whether this computation decides it succeeds or not.
For example, you can try
$ /usr/bin/test a = b
$ echo $?
1
$ /usr/bin/test a = a
$ echo $?
0
$ /usr/bin/test -f .bashrc
0
$ /usr/bin/test -f anonexistingfile
1
One notable difference between /usr/bin/test
and /usr/bin/[
is that /usr/bin/[
raises an error if the last argument is not ]
. Just of aesthetic reasons.
So, don't use [
like a sort of parenthesis for grouping logic. Don't nest them. Don't use them to enclose other commands. It makes no sense.
Use [
as you would use an external program, to which you pass arguments, and from which you expect an exit code.
Exactly like you to with grep -q
.
&&
is an operator separating two commands. Like ;
is. With a different behavior than ;
. a && b
runs command a. And then runs command b
, only if a
exit code what 0. And the exit code of a && b
is 0 iff exit code of both commands a
and b
where 0. Other wise it is the exit code of the first of them that failed.
And, lastly, if
is used with a command.
if command0
then
command1
else
command2
Executes command0. And then executes command1 if command0 was successful (had an exit code 0), or command2 otherwise.
So, with that in mind, in your case, your line
if [ [grep -q "code ENOTFOUND" "publish log"] && [ ! $i -eq $maxTry ] ];
should be
if grep -q "code ENOTFOUND" "publish log" && [ ! $i -eq $maxTry ] ;
then
...
All my historical introduction shows you that it is just the same as
if grep -q "code ENOTFOUND" "publish log" && test ! $i -eq $maxTry ;
then
...
It Executes command grep -q "code ENOTFOUND" "publish log"
- if it fails (exit code non-zero), then does not execute
[ ! $i -eq $maxTry ]
. And the whole if "condition" fails, and the else
part is executed
- if it succeeds (exit code 0), executes command
[ ! $i -eq $maxTry ]
- if it fails, the whole if "condition" fails, and the
else
part is executed
- if it succeeds, the whole if "condition" is a success, and the
then
part is executed.
Of course, the choice of [
for that command name, and the choice of requiring that it terminates by a ]
is to makes it look a little bit like classical condition in other languages. But to understand this correctly, and not to struggle about when to add some [
, it is important that you understand what if
, what &&
and what [
mean in bash. if
and &&
are deciding if and when to execute several commands, and what exit code the group of several commands has. [
aka test
is just one of the many command you may want to execute. A command that does nothing, except having an exit code.
Last remark: I don't comment on the rest of your code. Only on the line about which was your question. For example,
grep -q "code ENOTFOUND" "publish log"
that I copied&pasted from your code to mine, because you gave no hint about what it is supposed to do.
But I have some doubt that this is what you want. Isn't there a missing dot between publish and log here?
The -q
is right. It mean that grep will, like [
a command that does nothing (prints nothing) and just have an exit code. So suited for usage in a if.
But then, you seems to be looking for a string "code ENOTFOUND" in a file named "publish log".
Likewise, I am pretty sure
if grep -q "npm ERR!"
is not what you wanted. It waits for lines on standard input, and succeeds (has exit code 0) if one of them contains "npm ERR!". I am pretty sure you intended to grep the content of a file. Like maybe the same publish.log
as earlier.
And more generally I don't know exactly what you are trying to do. But I have the feeling that you are just trying to npm publish something over and over, until either it succeeds or you reach a maximum trial.
In which case
for ((i=0; i<$maxTry; i++))
do
npm publish $package
grep -q "code ENOTFOUND" publish.log || break
done
probably does it.
Maybe npm also has an exit code (well, it sure has. I just don't know whether it works for you. It returns 0 on success and non-0 on failure), and you could just do
for ((i=0; i<$maxTry; i++))
do
if npm publish $package
then
break
fi
done
or
for ((i=0; i<$maxTry; i++)); do
npm publish $package && break
done
tl;dr (but if you don't want to always struggle with that kind of thing, that tl;dr won't cut it. It is giving a fish instead of teaching fishing): correct syntax is
if grep -q "code ENOTFOUND" "publish log" && [ ! $i -eq $maxTry ] ;