Escaping in test comparisons - shell

In the following, I would like check if a given variable name is set:
$ set hello
$ echo $1
hello
$ echo $hello
$ [[ -z \$$1 ]] && echo true || echo false
false
Since $hello is unset, I would expect the test to return true. What's wrong here? I would assume I am escaping the dollar incorrectly.
TYIA

You are testing if \$$1 is empty. Since it begins with a $, it is not empty. In fact, \$$1 expands to the string $hello.
You need to tell the shell that you want to treat the value of $1 as the name of a parameter to expand.
With bash: [[ -z ${!1} ]]
With zsh: [[ -z ${(P)1} ]]
With ksh: tmp=$1; typeset -n tmp; [[ -z $tmp ]]
Portably: eval "tmp=\$$1"; [ -z "$tmp" ]
(Note that these will treat unset and empty identically, which is usually the right thing.)

Related

Checking if a branch exists in Jenkins shell script

I am having a trouble checking if a branch exists in a Jenkins job.
My shell scripts is as follows:
set +e
BRANCH_EXISTS=$(git ls-remote --quiet | grep -w ${BRANCH_NAME})
if [[ -z $"{BRANCH_EXISTS}" ]]
then
echo "Branch does not exist"
else
echo "Branch exists"
fi
Following is a log:
07:36:26 + BRANCH_EXISTS='4cbe2d1776db4dc263a1e7884e35da49a0a6f309 refs/heads/equalizer_fe_testing'
07:36:26 + [[ -z {BRANCH_EXISTS} ]]
07:36:26 + echo 'Branch exists'
07:36:26 Branch exists
...
07:36:27 + BRANCH_NAME=test/fe-testing_staging1
07:36:27 ++ grep -w test/fe-testing_staging1
07:36:27 ++ git ls-remote --quiet
07:36:28 + BRANCH_EXISTS=
07:36:28 + [[ -z {BRANCH_EXISTS} ]]
07:36:28 + echo 'Branch exists'
07:36:28 Branch exists
07:36:29 Finished: SUCCESS
As you can see, in one case I check for a branch that does not exist. But still the if condition passes. I have tried with if [[ -n $"{BRANCH_EXISTS}" ]] as well but still gets the same result.
What am I doing wrong here?
You are incorrectly using the test operator to check if string is empty. In most shells, variable interpolation happens with ${var} and not {var} or $"{var}"
$ var=foo
$ [[ -z $var ]] && echo ok
$ [[ -z $var ]] || echo ok
ok
$ var=
$ [[ -z $var ]] && echo ok
ok
$ [[ -z var ]] && echo ok # does not assert, because literal 'var' is not empty
When you are doing [[ -z {var} ]] you are not interpolating var, but comparing against a literal string {var} which is never empty.
Change your conditional to [[ -z "${BRANCH_EXISTS}" ]] to make it work as expected.
As Inian already explained in his answer, $"{BRANCH_EXISTS}", but "${BRANCH_EXISTS}" would be correct. However, there is more to it:
In your case, the double quotes are not necessary, because no word splitting occures here after parameter expansion (since you are inside a [[....]]. Hence you can write simply [[ -z $BRANCH_EXISTS ]].
However, if the branch does not exist, grep will return a non-zero exit code, and the program will be aborted (due to your set -e. Therefore, the then branch of your conditional won't ever be executed, even if you would fix the conditional.

Bash testing -n [duplicate]

Usually i only use [[ for all kinds of test cases, because it's the most advanced way and it's more safe to use (Regex, ...).
I know that [[ executes different code than [, but according to the manpage and various documentations, it should at least handle options like "-n" the same way, but it doesn't.
-n STRING the length of STRING is nonzero
VAR=
if [[ -n $VAR ]]
then
echo "\$VAR is nonzero"
else
echo "\$VAR is zero"
fi
$VAR is zero
VAR=
if [ -n $VAR ]
then
echo "\$VAR is nonzero"
else
echo "\$VAR is zero"
fi
$VAR is nonzero
How is this even possible?
bash 4.1.2(1)
I think that your problem is related to quotes.
When you use [ -n $VAR ] the command that is executed won't contain any argument where $VAR should be:
$ set -x
$ [ -n $VAR ]
+ '[' -n ']'
This means that you are essentially testing whether the string -n is non-empty, because the following two tests are equivalent:
[ string ] # is a shorthand for
[ -n string ] # which is always true!
If you use quotes, then you get different behaviour:
$ [ -n "$VAR" ]
+ '[' -n '' ']'
Now you are testing whether the variable is non-empty, so you get the expected behaviour.
Quoting. You have to quote variables that you use in [:
$ VAR=
$ [ -n $VAR ]
$ echo $?
0
$ [ -n "$VAR" ]
$ echo $?
1

Bash substitution: log when variable is not set

I use bash substitutions to give neat one-line validation for input, e.g.:
#!/bin/bash
export PARAM1=${1?Error, please pass a value as the first argument"}
# do something...
In some cases though, I want to only log a message when something is unset and then continue as normal. Is this possible at all?
Maybe something along the lines of
test -n "$1" && export PARAM1="$1" || log "\$1 is empty!"
should do; here the test clause returns true if and only if $1 is non-empty.
For regular parameters (in bash 4 or later), you can use the -v operator to check if a parameter (or array element, as of version 4.3) is set:
[[ -v foo ]] || echo "foo not set"
bar=(1 2 3)
[[ -v bar[0] ]] || echo "bar[0] not set"
[[ -v bar[8] ]] || echo "bar[8] not set"
Unfortunately, -v does not work with the positional parameters, but you can use $# instead (since you can't set, say, $3 without setting $1).
(( $# >= 3 )) || echo "third argument not set"
Before -v became available, you would need to compare two default-value expansions to see if a parameter was unset.
[[ -z $foo && ${foo:-bar} == ${foo-bar} ]] && echo "foo is unset, not just empty"
There's nothing special about bar; it's just an arbitrary non-empty string.

unset variable check in bash

I have two variables declared but unset:
__var1=
__var2=
Now I set __var2 to have some value:
__var2=1
When I try to do a check like this:
[ -z "$__var1" -a -z "$__var2" ] || echo "Both missing!"
I am getting that message Both missing!. But that's incorrect.
Why is that? And how to do a proper check, to see if both of them are missing?
And if the user wants to check if the variable is really unset and not just having an empty value, you can do:
$ A=1234
$ [[ -z ${A+.} ]] && echo "Variable is unset."
$ A=
$ [[ -z ${A+.} ]] && echo "Variable is unset."
$ unset A
$ [[ -z ${A+.} ]] && echo "Variable is unset."
Variable is unset.
In which in your case it could be
[[ -z ${__var1+.} && -z ${__var2+.} ]] && echo "Both variables are unset!"
#Dave Schweissguth's answer makes a good point about the logic of your code, but there are also things to observe about the syntax:
[Update: The original form of the question used assignments such as $__var1= - this has since been corrected] In Bourne-like/POSIX-compatible shells you do not use the $ prefix when assigning a value, only when referencing it; thus, your assignments should read:
__var1=
__var2= # or, later: __var2=1
Your question is tagged bash, so the best bash way to write your could would be:
[[ -z $__var1 && -z $__var2 ]] && echo "Both missing!"
Note the use of [[ ... ]] rater than [ ... ], which obviates the need to double-quote the operands to -z.
By contrast, the most portable (POSIX-compliant) way is:
[ -z "$__var1" ] && [ -z "$__var2" ] && echo "Both missing!"
Your code prints "Both missing!" if it's not true (||) that both (-a) variables are empty (-z). You want to print the message if that IS true. Do that like this:
[ -z "$__var1" -a -z "$__var2" ] && echo "Both missing!"
I don't recall ever seeing a version of bash or test (what sh uses to evaluate the same expressions) without -z or -a, so as far as I know the above will work on any Unix-like system you're likely to find.

Regarding Bash substring comparison

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

Resources