Would someone please care to explain how on earth this is supposed to make sense:
Contents of insanity.sh:
#!/bin/bash
ARG=""
if [ -n $ARG ]; then
echo string is greater than zero
fi
if [ -z $ARG ]; then
echo string is empty
fi
Running the script:
[USERNAME#login001 clusterUtils]$ ./insanity.sh
string is greater than zero
string is empty
Currently using this tutorial at the moment.
It is happening because you're not quoting $ARG inside [ ... ].
Without quoting your code is effectively running as:
if [ -n ]; then
echo string is greater than zero
fi
if [ -z ]; then
echo string is empty
fi
Any non-empty string between [ ... ] will evaluate to true, hence both if conditions are successful.
Fix: It is recommended to use [[ ... ]] as you're using bash:
arg=""
if [[ -n $arg ]]; then
echo 'string is greater than zero'
fi
if [[ -z $arg ]]; then
echo 'string is equal to zero, empty'
fi
[[ ... ]] doesn't require your variables to be quoted like [ ... ] sine [ is an external command and [[ ... ]] is a builtin bash construct.
Also avoid all uppercase variables in your script to avoid conflicts with reserved env variables.
Related
I am writing a bash script that sometimes will use environment variables GIT_DIR and GIT_WORK_TREE. The bash script can only operate correctly if either both variables exist or neither exist. In case there's a technical difference, it makes no difference
This works, but there has to be a better way:
if [[ -z "${GIT_DIR}" ]]; then
_GIT_DIR_EXISTS=0
else
_GIT_DIR_EXISTS=1
fi
if [[ -z "${GIT_WORK_TREE}" ]]; then
_GIT_WORK_TREE_EXISTS=0
else
_GIT_WORK_TREE_EXISTS=1
fi
if [[ "${_GIT_DIR_EXISTS}" -ne "${_GIT_WORK_TREE_EXISTS}" ]]; then
echo "GIT_DIR is ${GIT_DIR}"
echo "GIT_WORK_TREE is ${GIT_WORK_TREE}"
echo "Both or none must exist"
exit 1
fi
I tried:
if [[ (-z "${GIT_DIR}") -ne (-z "${GIT_WORK_TREE}") ]]; then
But that gives this error:
bash: syntax error in conditional expression
bash: syntax error near '-ne'
I then resorted to trying semi-random things, with varying errors:
if [[ -z "${GIT_DIR}" -ne -z "${GIT_WORK_TREE}" ]]; then
if [[ [-z "${GIT_DIR}"] -ne [-z "${GIT_WORK_TREE}"] ]]; then
if [[ [[-z "${GIT_DIR}"]] -ne [[-z "${GIT_WORK_TREE}"]] ]]; then
if [[ -z "${GIT_DIR}" ]] ^ [[ -z "${GIT_WORK_TREE}" ]]; then
if { [[ -z "${GIT_DIR}" ]] } -ne { [[ -z "${GIT_WORK_TREE}" ]] }; then
if [[ (( -z "${GIT_DIR}" )) -ne (( -z "${GIT_WORK_TREE}" )) ]]; then
I tried:
if [[ $(test -z "${GIT_DIR}") -ne $(test -z "${GIT_WORK_TREE}") ]]; then
But realized that doesn't work because it's a sub-process, and they'd need to be exported. as Socowl comments, this compares the outputs of the test commands which output nothing, not their exit statuses.
I apologize if this is a duplicate. I've searched here and google for a while, and must not be using the right terminology.
How about this:
if [[ "${GIT_DIR:+set}" != "${GIT_WORK_TREE:+set}" ]]; then
echo "GIT_DIR is '${GIT_DIR}'"
echo "GIT_WORK_TREE is '${GIT_WORK_TREE}'"
echo "Both or none must exist"
exit 1
fi
Explanation: ${var:+value} is a variant of parameter expansion that gives "value" if var is set to a nonempty string, or the empty string if var is unset or empty. So if both vars are unset/empty, it becomes if [[ "" != "" ]]; then, and if they're both set it becomes if [[ "set" != "set" ]]; then etc.
BTW, if you want to test whether the variables are set at all (even if to the empty string), use ${var+value} (note the lack of colon). The bash manual lists the :+ version, but not the + version.
I'm trying to write a short bash script that optionally accepts arguments from the command line, or prompts for their input
if [ [ -z "$message" ] && [ -z "$predefined" ] ] ; then
read -p "Enter message [$defaultMessage]: " message
message=${message:-$defaultMessage}
else
if [ -n "$predefined" ]; then
if [ -f $base/$environment/vle/data/$predefined.txt ]; then
echo Predefined message file $predefined.txt does not exist
exit 1
fi
fi
fi
If neither message nor predefined has been passed in as command line arguments, then the code should prompt for a value for message; otherwise if predefined has been passed in as a command line argument, then the script should test for the existence of a file of that name and only continue if the file does exist
But I'm getting the following error
[: -z: binary operator expected
at the first of those if tests
Any help in explaining what's wrong with my syntax for that first if statement? Or providing an alternative syntax to achieve the same objectives.
The first if is not well-formed. This would work:
if [ -z "$message" ] && [ -z "$predefined" ]; then
or this:
if test -z "$message" && test -z "$predefined"; then
or this bash-specific, easy but dirty way:
if [[ -z "$message" ]] && [[ -z "$predefined" ]]; then
or this bash-specific proper way:
if [[ -z $message && -z $predefined ]]; then
In this last version the double-quotes are unnecessary, not a typo.
Thanks #mklement0 for the corrections in the bash-specific style, and for this final comment:
I should note that there's one case where double-quoting is still a must inside [[ ... ]], namely if you want a variable reference on the right side of a string comparison (==) to be treated as a literal:
v='[a]'
[[ $v == $v ]] # FALSE!
[[ $v == "$v" ]] # true
Without double-quoting, the right-hand side is interpreted as a pattern. Some people advocate always double-quoting variable references so as not to have to remember such subtleties. That said (from bash 3.2 on), you must NOT double-quote the right operand when regex matching with =~
test expression1 -a expression2
is true if both expressions are true.
test expression1 -o expression2
is true if either or both expressions are true.
if [ -z "$message" -a -z "$predefined" ]; then
read -p "Enter message [$defaultMessage]: " message
message=${message:-$defaultMessage}
else
if [ -n "$predefined" -a -f $base/$environment/vle/data/$predefined.txt ]; then
echo Predefined message file $predefined.txt does not exist
exit 1
fi
fi
This was able to combine 4 test into 2 while also getting rid of one nested if expression; then ; fi
I try to test if a string starts with a certain prefix. But my script seems not work (I would expect the "if" branch will not get run). Can some Bash expert help to take a look? thanks!
Here is my code and test result:
$ cat testb.bash
#!/bin/bash
my_var="abcdefg";
if [[ "${my_var:0:5}"=="order" ]]; then
echo "value of my_var is ${my_var}.";
fi;
if [[ "${my_var:0:5}" -eq "order" ]]; then
echo "value of my_var is ${my_var}.";
fi;
if [ "${my_var:0:5}"="order" ]; then
echo "value of my_var is ${my_var}.";
fi;
$ bash -x testb.bash
+ my_var=abcdefg
+ [[ -n abcde==order ]]
+ echo 'value of my_var is abcdefg.'
value of my_var is abcdefg.
+ [[ abcde -eq order ]]
+ echo 'value of my_var is abcdefg.'
value of my_var is abcdefg.
+ '[' abcde=order ']'
+ echo 'value of my_var is abcdefg.'
value of my_var is abcdefg.
$
Whitespace is significant in this case. As you can see in the -x output, it understands the first condition as
[[ -n "${my_var:0:5}==order" ]]
Moreover, to test for a prefix, you can use a pattern:
[[ $my_var == order* ]]
To test the existence of substring, you can use either of these:
if [[ "$j" =~ string1 ]]; then
if [[ $j == *string1* ]]; then
In your particular case, you miss a space surounding ==, so instead of
if [[ "${my_var:0:5}"=="order" ]]; then
it should be
if [[ "${my_var:0:5}" == "order" ]]; then
^ ^
Finally, note that your condition was evaluated as true because it was evaluating if [ "string" ], which is true if string is not empty:
$ [ "a" ] && echo "yes"
yes
Test
$ cat a
#!/bin/bash
my_var="abcdefg";
if [[ "${my_var:0:5}" == "order" ]]; then
echo "value of my_var is ${my_var}."
elif [[ "${my_var:0:5}" == "abcde" ]]; then
echo "yeahaa"
else
echo "is not"
fi
$ ./a
yeahaa
Ok, i tested your code, you shoud such as the following code:
prefix="pre_order";
pre="pre_"
len=${#pre}
echo $len
if [[ "${prefix:0:len}" == "blahvlah" ]] ; then
echo "dddd"
fi;
Notes:
use == for string comparation
for ${} you should initilize a string variable before ${}
use len=${#pre} for lenght of string.
A POSIX-compliant way to test for a prefix is to attempt to remove the prefix, and compare the result to the original string. If the two are the same, the prefix is not present, the removal fails, and the expression expands to the original string.
prefix=foo
string=foobar
if [ "${string#$prefix}" = "$string" ]; then
printf "$string does not start with $prefix\n"
else
printf "$string starts with $prefix\n"
fi
The Bash Reference Manual says that
[ string ]
and
[ -n string ]
will both return true if the string 's length is not 0
but the fact is not as so
greet=
if [ $greet ]; then
echo '1'
else
echo '2'
fi
if [ -n $greet ]; then
echo '1'
else
echo '2'
fi
the output is
2
1
the Bash Reference Manual just says
-n string
string
True if the length of string is non-zero.
so, what the real difference between the two form?
As #user1502952 said, you need to use double-quotes; but let me explain why. Suppose you execute:
greet=
[ -n $greet ] && echo "it's nonblank"
When the shell parses the [ -n $greet ] part, it expands $greet to the empty string, and then does word splitting. For instance, if $greet expanded to something with spaces in the middle, it would treat each "word" as a separate argument to the [ command. In this case, however, $greet expands to nothing, which contains no "word"s at all, and hence is treated as zero arguments to [ -- it effectively vanishes from the command. So [ -n $greet ] is equivalent to [ -n ], which checks to see if the string "-n" is nonblank. It is, so it evaluates to true.
Compare this with [ -n "$greet" ]: in this case, the double-quotes allow the expansion of $greet, but prevent word splitting. So the [ command actually gets a zero-length second argument, realizes that -n is supposed to be an operator, and gets the expected answer.
when you are using -n option, it is required to use double quotes.
if [ -n "$greet" ]
as the string is empty the above expression evaluates to false, as the length is zero.
if [ "$greet" ]
this also evaluates to false as the string is empty.
Moreover to check for empty string, -z option can be used.
if [ -z "$greet" ]
this will be true as the string is empty.
Check this link too: http://tldp.org/LDP/abs/html/comparison-ops.html
Bash performs word splitting inside [ but not inside [[, so you don't have to quote parameters if you use [[:
$ x=
$ [ -n $x ]; echo $?; [ -n "$x" ]; echo $?
0
1
$ [[ -n $x ]]; echo $?; [[ -n "$x" ]]; echo $?
1
1
type shows [[ $x ]] as [[ -n $x ]]:
$ f() { [[ $x ]]; }; type f
f is a function
f ()
{
[[ -n $x ]]
}
What is the difference between [ test ] and [[ test ]] in bash?
When is one more appropriate than the other and what does the ; at the end do?
if [[ -z $DIRECTORY ]];
then
DIRECTORY=html
fi
if [ ! -d "$DIRECTORY" ]; then
echo installation directory "'${DIRECTORY}'" does not exist
exit 1
fi
[[ is a bash keyword similar to (but more powerful than) the [ command. See http://mywiki.wooledge.org/BashFAQ/031 and http://mywiki.wooledge.org/BashGuide/TestsAndConditionals Unless you're writing for POSIX sh, we recommend [[.
We usually use single square brackets when we:
Check something with files and want to use patterns (e.g. asterisk): if [ -L $file ]; then
Check artithmetic expressions: if [ $a -lt $b ]; then
Check something with strings and want to use " " and treat special characters as normal (e.g. asterisk): if [ -z "$string" ]; then
We usually double square brackets when we:
Want to use pattern with string (e.g. asterisk): if [[ "$string1" == *[sS]tring* ]]; then
Block patterns in file names (e.g. asterisk) e.g. we search file named *.sh: if [[ -a *.sh ]]; then
Want to use operators && and ||: if [[ $a == 3 || $b == 4]]; then
Don't want to put strings in " "
[ is for shell, [[ is for bash.
For example :
Try [ $A -eq 1 ]: if $A is not set, it raise an error.
[[ $A -eq 1 ]] will works.