Can anyone tells me what does this script means found in a .sh file:
[ ! -n "$T_R" ] && echo "Message Appear" && exit 1;
Edit: Correcting for misinformation pointed out by tripleee
The brackets [ ]
are an alias for 'test', which tests whether a condition is met. Not to complicate matters, but do note that this is discrete from the the bash shell keyword [[ ]] (Thanks, tripleee for clearing that up!). See This post for further details. These days, most people seem to use the latter due to its more robust feature set.
Between the brackets, the script is testing to determine whether the variable "$T_R" is an empty string.
The -n operator returns true if the length of the string passed to it as an argument is non-empty.
The ! inverts the case (the test succeeds if the result is not
true). So in this case, test suceeds (returns 0) if the length of
the string variable "$T_R" is **not non-zero ** (i.e. if the
variable is an empty-string, or is non-existant).
The double-ampersand, && operator means only execute the subsequent code in the event of success, so the message "Message Appear" will only be echoed in the event the test succeeds (again, if "$T_R" is empty or unset).
Finally, the && exit 1 says to exit returning status 1 after successfully echoing the Message Appear message.
The bash and test man pages are extremely helpful on all of these topics and should be consulted for further details.
The chained && is a common short-circuit idiom.
Instead of writing
if true; then
if true; then
echo moo
fi
fi
you can abbreviate to just true && true && echo moo.
echo will usually succeed so true && echo moo && exit 1 will execute both the echo and the exit if true succeeds (which obviously it always will).
(There are probably extreme corner cases where echo could fail, but if that happens, you are toast anyways so I don't think it makes sense to try to guard against those.)
The [ is an alias for test which is a general comparison helper for shell scripts (in Bash, it's even a built-in). test -n checks whether a string is non-empty.
! is the general negation operator, so it inverts the test to checking for an empty string.
(This is slightly unidiomatic, because there is a separate test -z "$T_R" which checks specifically for the string being empty.)
Related
(I'm not a linux guy) and I want to check the status of a service when its updating (takes about 10 minutes) to make sure it is successful. I use a function to run the status command and while loop as follow:
get_status() { echo ...my command runs here and return the statue; }
I simply can get the status like $(get_status). Now I want to see what is the status and take action:
while $(get_status) == "PENDING"; do echo retrying... && sleep 5; done
I've tried different ways like single/double brackets but cannot get the while comparison to work properly? Can anybody help please?
The while loop doesn't know anything about comparison tests. It only knows how to check the exit status of a command, and $(get_status) == "PENDING" is not a command. The brackets you want are for either a test command or a bash conditional expression command.
while test "$(get_status)" = "PENDING"; do
or
# [ is a synonym for test, with the added requirement that
# there be a final argument ] to complete the illusion of
# syntax.
while [ "$(get_status) = "PENDING" ]; do
or
while [[ $(get_status) == "PENDING" ]]; do
In the first two cases, = is preferred as the correct equality operator for test/[. In the last case, == may be used, and the quotes can be dropped around $(get_status) because no word-splitting or filename generation is performed on expansions in [[ ... ]]. (The quotes could be dropped around the literal word PENDING in all three cases, but could remain necessary for some right-hand arguments inside [[ ... ]] for reasons beyond the scope of this question.)
I found this code in an autoconf configure script. What is the following code trying to do?
if ${am_cv_autoconf_installed+:} false; then :
$as_echo_n "(cached) " >&6
else
Lots of stuff going on here. Let's break it down.
First of all, the syntax ${var+foo} is a common idiom for checking whether the variable var has been defined. If var is defined, then ${var+foo} will expand to the string foo. Otherwise, it will expand to an empty string.
Most commonly (in bash, anyway), this syntax is used as follows:
if [ -n "${var+foo}" ]; then
echo "var is defined"
else
echo "var is not defined"
fi
Note that foo is just any arbitrary text. You could just as well use x or abc or ilovetacos.
However, in your example, there are no brackets. So whatever ${am_cv_autoconf_installed+:} expands to (if anything) will be evaluated as a command. As it turns out, : is actually a shell command. Namely, it's the "null command". It has no effect, other than to set the command exit status to 0 (success). Likewise, false is a shell command that does nothing, but sets the exit status to 1 (failure).
So depending on whether the variable am_cv_autoconf_installed is defined, the script will then execute one of the following commands:
: false
-OR-
false
In the first case, it calls the null command with the string "false" as an argument, which is simply ignored, causing the if statement to evaluate to true. In the second case, it calls the false command, causing the if statement to evaluate to false.
So all this is really doing is checking whether am_cv_autoconf_installed is defined. If this were just an ordinary bash script and didn't require any particular level of portability, it would have been a lot simpler to just do:
if [ -n "${am_cv_autoconf_installed+x}" ]; then
However, since this is a configure script, it was no doubt written this way for maximum portability. Not all shells will have the -n test. Some may not even have the [ ] syntax.
The rest should be fairly self-explanatory. If the variable is defined, the if statement evaluates to true (or more accurately, it sets the exit status to 0), causing the $as_echo_n "(cached) " >&6 line to execute. Otherwise, it does whatever is in the else clause.
I'm guessing $as_echo_n is just the environment-specific version of echo -n, which means it will print "(cached) " with no trailing newline. The >&6 means the output will be redirected to file descriptor 6 which presumably is set up elsewhere in the script (probably a log file or some such).
I am curious about the logic behind && in continuous commands execution in shell.
Lets see an example: command_a && command_b && command_c
In shell, this means that once a command fails, the consequent commands will not execute and the chain should stop.
If we replace each command by its exit code, the chain becomes, for example this expression 0 && 1 && 0. (command_a succeed, command_b failed, command_c succeed)
If we try evaluating this expression, the evaluation should stop right after the first command, 0 value.
If && logic is replaced by ||, the expression would be more fit to the meaning of original chained command.
0 || 1 || 0.
Expression's evaluation stops after command_b execution
There's a difference between the semantics of a successful command and the representation of success via the numeric value of the exit code. If you consider the abstract "is a command successful", && makes more sense, since the "success" abstract boolean is true. That's why && is used. You need A to run, AND you need B to run.
But, due to the fact that there's usually only one status for success, but many different types of errors, the value 0 has been defined long ago to be used for exit status to indicate success.
So, the exit status of a command can simply not replace the command itself in such an expression. The semantics and the representation are just different.
Check out this post.
"The right side of && will only be evaluated if the exit status of the left side is zero. || is the opposite: it will evaluate the right side only if the left side exit status is nonzero..."
$ false && echo howdy!
$ true && echo howdy!
howdy!
$ true || echo howdy!
$ false || echo howdy!
howdy!
I have seen || used in some shell script too. I think it depends on the occasions. You may use command-a && command-b when you want command-b to be executed only after command-a success.
Likewise, you may use command-a || command-b to deal with the situation when command-a fails.
Update: After reading your question three times I now understand what puzzles you: That 0 represents success, and/or (sic) that logical operators treat it as true. Yes, that can be confusing, coming from C. The other Peter's answer explains that well. I let my original answer stand anyway because it is not "wrong".
It is what logic dictates if commands are impossible to perform or don't make sense unless their predecessors succeeded. That happens quite often: "Retrieve a HTML document from the web, verify the time stamp, parse a value out of it, and write that value as an updated data somewhere." Each step depends on the success of all preceding steps.
If, instead, you have something "retrieve a document from the web, or from the cache, or from disk, or take a default here document, in this order of preference", then the appropriate way to write that is indeed with logical ORs. This happens but is less common.
ORing is a common idiom though for error handling, because subsequent commands are exactly performed if the pervious failed. Consider Perl's idiomatic cmd() || die();. (You have more lives. Phew.)
I'm trying to figure what exactly is the bash code mentioned below trying to do, specially the [-z $M ] part. here M is a variable with a value
if [ -z $M ] ; then
can not find module directory
exit 1
man test Enter
press /-zEnter
you see:
-z STRING
the length of STRING is zero
so your script does, if $M length==0, then exit with status code 1
As others have said, it's using the test command (aka [) to check whether a string is blank. At least, that's what it's trying to do; because the string ($M) isn't double-quoted, it's actually doing something slightly different. Without double-quotes, the value of $M undergoes word splitting and wildcard expansion after it's replaced, so it might not be treated as a simple string (which the -z operator works on) with ... potentially unexpected consequences. Let me run through some of the possibilities:
If the value of $M is a single word (non-blank) without wildcards (* and ?), everything works as expected.
If the value of $M is zero-length (blank), the test command only sees a single argument (-z); when test is only given a single argument, it simply tests whether it's blank -- it's not, so it evaluates to true.
This happens to be the expected result in this case, but it's purely by coincidence, and with many other operators it wouldn't be the right result. For instance, [ -n $M ] (which looks like it should test whether $M is *non*blank), [ -e $M ] (which looks like it should test whether $M is the name of a file/directory) etc will all evaluate to true if $M is blank.
If the value of $M consists entirely of whitespace (but isn't empty), it gets eliminated before test sees it, and test evaluates to true (see previous case). This might or might not be what the scripter had in mind.
If the value of $M has multiple words, test will attempt to evaluate it as (part of) an expression. It will probably not be a valid expression, in which case test will print an error and return false (which is right ... sort of).
On the other hand, if it is a valid expression... Suppose for example you had, M='= -z; test would evaluate the expression -z = -z which would be true, not at all what the scripter had in mind.
If the value of $M has any wildcards, the shell will try to match them against files and pass test the list of matches; it'll try to evaluate them as an expression (see previous case), probably giving an error and returning false (again, sort of right).
Mind you, if you happen to have set the nullglob shell option and the wildcard doesn't match any files, the shell will replace it with null, and the script will act as though "u*n*m*a*t*c*h*e*d" was the empty string.
The lesson here: if you don't want your scripts to behave in weird and unexpected ways, double-quote your variable references!
The [ is actually a standard Unix command (probably implemented internally in Bash, but available whatever shell you are using). It is an alias for the command test, so its manual entry can be found by typing man test. Here's an online copy of that manual page.
When invoked as [, test will generally expect its last argument to be a ], just for good looks, so [ -z $M ] is equivalent to test -z $M.
In this case, the -z argument causes test to return true if the following argument is a string of length zero. The variable $M, defined further up the script, can thus be tested for a valid value.
It checks whether the content of variable M is an empty string.
Check this link
This question already has answers here:
How can I declare and use Boolean variables in a shell script?
(25 answers)
Checking the success of a command in a bash `if [ .. ]` statement
(1 answer)
Closed 5 years ago.
I have defined the following variable:
myVar=true
now I'd like to run something along the lines of this:
if [ myVar ]
then
echo "true"
else
echo "false"
fi
The above code does work, but if I try to set
myVar=false
it will still output true.
What might be the problem?
edit: I know I can do something of the form
if [ "$myVar" = "true" ]; then ...
but it is kinda awkward.
Thanks
bash doesn't know boolean variables, nor does test (which is what gets called when you use [).
A solution would be:
if $myVar ; then ... ; fi
because true and false are commands that return 0 or 1 respectively which is what if expects.
Note that the values are "swapped". The command after if must return 0 on success while 0 means "false" in most programming languages.
SECURITY WARNING: This works because BASH expands the variable, then tries to execute the result as a command! Make sure the variable can't contain malicious code like rm -rf /
Note that the if $myVar; then ... ;fi construct has a security problem you might want to avoid with
case $myvar in
(true) echo "is true";;
(false) echo "is false";;
(rm -rf*) echo "I just dodged a bullet";;
esac
You might also want to rethink why if [ "$myvar" = "true" ] appears awkward to you. It's a shell string comparison that beats possibly forking a process just to obtain an exit status. A fork is a heavy and expensive operation, while a string comparison is dead cheap. Think a few CPU cycles versus several thousand. My case solution is also handled without forks.