I have an if statement within a loop. It's set to false initially so I insert a timestamp in a file at the first run of the loop.
I can't seem to get the following to evaluate correctly.
$ConnectionIsCurrently=false
if ! [ $ConnectionIsCurrently ]; then
# changing false to true so this only occurs once.
$ConnectionIsCurrently=true
fi
Here is the full loop:
while [ $i -le $NoOfTests ]; do
ping -c1 -t1 www.google.ie > /dev/null
if [ $? = 0 ]; then
ConTestPASSCount=$((ConTestPASSCount+1))
if ! [ $ConnectionIsCurrently ]; then
printf 'PASSED AT: '
date "+%s"
printf 'PASSED AT: ' >> $directory$LogFile
date "+%s" >> $directory$LogFile
ConnectionIsCurrently=true
fi
echo "PASSCount $ConTestPASSCount"
else
ConTestFAILCount=$((ConTestFAILCount+1))
if [ $ConnectionIsCurrently ]; then
printf 'FAILED AT: '
date "+%s"
printf 'FAILED AT: ' >> $directory$LogFile
date "+%s" >> $directory$LogFile
ConnectionIsCurrently=false
fi
echo "FAILCount $ConTestFAILCount"
fi
sleep 1
Testcount=$((Testcount+1))
i=$((i+1))
done
The shell doesn't have boolean values, it just operates on strings (or numbers in $(())). The syntax:
if [ $ConnectionIsCurrently ]
tests whether $ConnectionIsCurrently is a non-empty string, and "false" is not empty.
You could use an empty value as falsey, and any non-empty value as truthy.
ConnectionIsCurrently=
if ! [ "$ConnectionIsCurrently" ]; then
ConnectionIsCurrently=true
fi
Note also that you don't put $ before the variable name when you're assigning to it, only when you're reading it. And you should generally quote variables, unless you're sure you want word splitting done. This is especially important when the variable could be empty, as in this case; without the quotes, the [ command doesn't receive any parameter there.
false and true are actually commands (and also bash builtins), so you can run them as commands and act on the exit status:
ConnectionIsCurrently=false
if ! $ConnectionIsCurrently; then
# changing false to true so this only occurs once.
ConnectionIsCurrently=true
fi
The [...] are not required syntax for the if command: [ is just a regular command whose exit status is used by if.
To summarize:
if and while execute a command and branch depending on whether that command succeeds or fails.
false is a command that produces no output and always fails.
true is a command that produces no output and always succeeds.
[ is a command that succeeds or fails depending on the evaluation of the expression preceding the closing ] argument; man test or info test for details. With a single argument (which should be enclosed in double quotes) before the ], [ succeeds if and only if the argument is non-empty. The [ command is typically built into the shell, but it acts like a command; it's not a special shell syntax.
The shell (sh, bash, ksh, zsh) does not have built-in Boolean types or values. There are several common idioms for using Booleans in shell scripts.
A. Assign a variable the string value true or false. Using such a value in an if statement will do the right thing. (This method is my personal favorite.) Note that the strings true and false are the names of commands, not arbitrary strings.
foo=true
if $foo ; then echo OK ; else echo Oops ; fi
B. Assign a variable any arbitrary non-empty value for truthiness, or the empty string (or leave it unset) for falsitude:
foo=yes
if [ "$foo" ] ; then echo OK ; else echo Oops ; fi
foo=""
if [ "$foo" ] ; then echo Oops ; else echo OK ; fi
(The shell treats an unset variable as if it were set to the empty string -- unless you've done set -o nounset, but that's not usually done in scripts.)
C. Pick two arbitrary strings to represent truth and falsehood, and use them consistently. Use string comparisons to test.
foo=TRUE
if [ "$foo" = TRUE ] ; then echo OK ; else echo Oops ; fi
foo=FALSE
if [ "$foo" = TRUE ] ; then echo Oops ; else echo OK ; fi
All of these methods are potentially error-prone. If you forget a $ or misspell one of your conventional strings, you can get bad results with no warning from the shell; for example with method C, the string True will silently be treated as a false condition. Languages with strictly behaving Booleans can avoid these problems. Bash is not such a language.
I would like to know how to assign a variable inside if block in shell script..
Below is my code..
if [[ -z "$MMBOX_PATH" || -z "$BACKUP_PATH" || -z "$REMOTE_SERVER" || -z "$LOG_PATH" ]]
then
echo -e "Must Provide All Required Paths [$FLAG is Empty].."
exit 1
fi
The above code will run whenever it found empty variable, but I also wants to know which variable is empty (E.g., In above code suppose if LOG_PATH variable is empty then it should display in echo output in place of $FLAG )
I tried following codes..
if [[ `FLAG='MMBOX_PATH'` && -z "$MMBOX_PATH" || `FLAG='BACKUP_PATH'` && -z "$BACKUP_PATH" || `FLAG='REMOTE_SERVER'` && -z "$REMOTE_SERVER" || `FLAG='LOG_PATH'` && -z "$LOG_PATH" ]]
then
echo -e "Must Provide All Required Paths [$FLAG is Empty].."
exit 1
fi
But above code returns false hence it is not printing the content inside echo.
I also tried to keep FLAG variable before condition execution, but every time it returns 'Nothing'
if FLAG='MMBOX_PATH' && [[ -z "$MMBOX_PATH" ]]
then
echo -e "Must Provide All Required Paths [$FLAG is Empty].."
exit 1
fi
In above case I'm getting FLAG='MMBOX_PATH' in output but if I add one more condition to that if nothing is printing (Means if I check same thing for BACKUP_PATH,REMOTE_SERVER..)
if FLAG='MMBOX_PATH' && [[ -z "$MMBOX_PATH" ]] && FLAG='LOG_PATH' && [[ -z "$LOG_PATH" ]]
then
echo -e "Must Provide All Required Paths [$FLAG is Empty].."
exit 1
fi
In this case nothing is printing even though MMBOX_PATH present and LOG_PATH empty.
Note: Using if condition each and every variable it is possible to know which variable is empty,but I don't want to extend my lines with if-else conditions I just want to know in that if block itself how to assign a variable and prints once condition is true.
Can anybody help me how to get empty variable..? (/bin/bash)
If all you are doing is checking existence with the if you could use a function.
check() {
for i in "$#";do
if [[ -z "${!i}" ]]
then
echo -e "Must Provide All Required Paths [\$$i is Empty].."
exit 1
fi
done
}
check MMBOX_PATH BACKUP_PATH REMOTE_SERVER LOG_PATH
Shell already provides a syntax for verifying that a variable has a value and exits if it does not:
: ${MMBOX_PATH:?Must provide MMBOX_PATH}
: ${BACKUP_PATH:?Must provide BACKUP_PATH}
: ${REMOTE_SERVER:?Must provide REMOVE_SERVER}
: ${LOG_PATH:?Must provide LOG_PATH}
There's no need to define a check function that does the same thing.
The initial colon is the do-nothing command; the shell evaluates its arguments, and : exits with status 0 immediately. The parameter expansion is what verifies that the named parameter has a value. If it does not, the given error message is printed. If the shell is not interactive, it also exits with status 1.
How would be the correct bash syntax for something like this:
if [ "$actual" == "$expected" ]; then
doNothing
else
echo "Error: actual: $actual. Expected: $expected"
fi
I am looking for something that works for all possible values of the variables "actual" and "expected". The content of the variables must not be interpreted/evaluated/expanded in any way. The script does not need to be portable (a bash only solution is ok).
You could use the simplest do-nothing statement available:
if [ "$actual" = "$expected" ]; then
:
else
echo "Error: actual: $actual. Expected: $expected"
fi
(Note: One = not two in [/test.)
But a better idea is to just invert the test and remove the need for that entirely:
if [ "$actual" != "$expected" ]; then
echo "Error: actual: $actual. Expected: $expected"
fi
Did you try:
if [ "$actual" != "$expected" ]; then
echo "Error: actual: $actual. Expected: $expected"
fi
if [[ $actual != $expected ]]
then
echo "Error: actual: $actual. Expected: $expected"
fi
Using the builtin [[ has several advantages over test / [. For one, you don't get bitten if you don't quote variables containing whitespace.
[[ ]] also offers < and > for locale-aware lexicographic sorting, regular expression matching, and =~. Check man bash.
(Note Etan's comment though on at least one dissenting opinion. I haven't yet made up my mind whether this is a disadvantage or a feature to be exploited, but it is sure surprising.)
There's also the thing with putting then on a separate line, but that's just personal preference.
vars="a,b"
a="True"
b="False"
IFS=","
for var in $vars; do
if [[ "$var" = "True" ]]; then
echo "True found"
fi
done
I would expect the above bash script to print out "True found". But it does not print anything. Any ideas as to why ?
Make this:
if [[ "${!var}" = "True" ]]; then
${!varname} expands the variable named in $varname. Otherwise, you get the name itself, not the contents of the variable with that name.
See BashFAQ #6 for far more details.
If I want to check for the null string I would do
[ -z $mystr ]
but what if I want to check whether the variable has been defined at all? Or is there no distinction in Bash scripting?
I think the answer you are after is implied (if not stated) by Vinko's answer, though it is not spelled out simply. To distinguish whether VAR is set but empty or not set, you can use:
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
You probably can combine the two tests on the second line into one with:
if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
However, if you read the documentation for Autoconf, you'll find that they do not recommend combining terms with '-a' and do recommend using separate simple tests combined with &&. I've not encountered a system where there is a problem; that doesn't mean they didn't used to exist (but they are probably extremely rare these days, even if they weren't as rare in the distant past).
You can find the details of these, and other related shell parameter expansions, the test or [ command and conditional expressions in the Bash manual.
I was recently asked by email about this answer with the question:
You use two tests, and I understand the second one well, but not the first one. More precisely I don't understand the need for variable expansion
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
Wouldn't this accomplish the same?
if [ -z "${VAR}" ]; then echo "VAR is not set at all"; fi
Fair question - the answer is 'No, your simpler alternative does not do the same thing'.
Suppose I write this before your test:
VAR=
Your test will say "VAR is not set at all", but mine will say (by implication because it echoes nothing) "VAR is set but its value might be empty". Try this script:
(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo "JL:1 VAR is not set at all"; fi
if [ -z "${VAR}" ]; then echo "MP:1 VAR is not set at all"; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo "JL:2 VAR is not set at all"; fi
if [ -z "${VAR}" ]; then echo "MP:2 VAR is not set at all"; fi
)
The output is:
JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all
In the second pair of tests, the variable is set, but it is set to the empty value. This is the distinction that the ${VAR=value} and ${VAR:=value} notations make. Ditto for ${VAR-value} and ${VAR:-value}, and ${VAR+value} and ${VAR:+value}, and so on.
As Gili points out in his answer, if you run bash with the set -o nounset option, then the basic answer above fails with unbound variable. It is easily remedied:
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
Or you could cancel the set -o nounset option with set +u (set -u being equivalent to set -o nounset).
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~>
-z works for undefined variables too. To distinguish between an undefined and a defined you'd use the things listed here or, with clearer explanations, here.
Cleanest way is using expansion like in these examples. To get all your options check the Parameter Expansion section of the manual.
Alternate word:
~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED
Default value:
~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED
Of course you'd use one of these differently, putting the value you want instead of 'default value' and using the expansion directly, if appropriate.
Advanced Bash scripting guide, 10.2. Parameter Substitution:
${var+blahblah}: if var is defined, 'blahblah' is substituted for the
expression, else null is substituted
${var-blahblah}: if var is defined, it is itself substituted, else
'blahblah' is substituted
${var?blahblah}: if var is defined, it is substituted, else the
function exists with 'blahblah' as an error message.
To base your program logic on whether the variable $mystr is defined or not, you can do the following:
isdefined=0
${mystr+ export isdefined=1}
Now, if isdefined=0 then the variable was undefined, and if isdefined=1 the variable was defined.
This way of checking variables is better than the previous answers, because it is more elegant, readable, and if your Bash shell was configured to error on the use of undefined variables (set -u), the script will terminate prematurely.
Other useful stuff:
To have a default value of 7 assigned to $mystr if it was undefined, and leave it intact otherwise:
mystr=${mystr- 7}
To print an error message and exit the function if the variable is undefined:
: ${mystr? not defined}
Beware here that I used ':' so as not to have the contents of $mystr executed as a command in case it is defined.
A summary of tests.
[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set" # may or may not be empty
[ -n "${var+x}" ] && echo "var is set" # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"
The explicit way to check for a variable being defined would be:
[ -v mystr ]
Test if a variable is set in bash when using "set -o nounset" contains a better answer (one that is more readable and works with set -o nounset enabled). It works roughly like this:
if [ -n "${VAR-}" ]; then
echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
echo "VAR is set, but empty"
else
echo "VAR is not set"
fi
Another option: the "list array indices" expansion:
$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1
The only time this expands to the empty string is when foo is unset, so you can check it with the string conditional:
$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0
should be available in any Bash version 3.0 or greater.
The Bash Reference Manual is an authoritative source of information about Bash.
Here's an example of testing a variable to see if it exists:
if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi
(From section 6.3.2.)
Note that the whitespace after the open [ and before the ] is not optional.
Tips for Vim users
I had a script that had several declarations as follows:
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
But I wanted them to defer to any existing values. So I rewrote them to look like this:
if [ -z "$VARIABLE_NAME" ]; then
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi
I was able to automate this in Vim using a quick regex:
s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r export \1=\2\rfi\r/gc
This can be applied by selecting the relevant lines visually, then typing :. The command bar pre-populates with :'<,'>. Paste the above command and hit Enter.
It was tested on this version of Vim:
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root#apple.com
Windows users may want different line endings.
Not to shed this bike even further, but wanted to add
shopt -s -o nounset
is something you could add to the top of a script, which will error if variables aren't declared anywhere in the script.
The message you'd see is unbound variable, but as others mention, it won't catch an empty string or null value.
To make sure any individual value isn't empty, we can test a variable as it's expanded with ${mystr:?}, also known as dollar sign expansion, which would error with parameter null or not set.
Here is what I think is a much clearer way to check if a variable is defined:
var_defined() {
local var_name=$1
set | grep "^${var_name}=" 1>/dev/null
return $?
}
Use it as follows:
if var_defined foo; then
echo "foo is defined"
else
echo "foo is not defined"
fi
A shorter version to test an undefined variable can simply be:
test -z ${mystr} && echo "mystr is not defined"
Call set without any arguments... it outputs all the defined variables.
The last ones on the list would be the ones defined in your script.
So you could pipe its output to something that could figure out what things are defined and what’s not.