Comparing strings in if and using or - bash

This code is not working, but I don't know what's wrong.
If I only use single brackets the string isn't compared right.
#!/bin/bash
forceupdate=false
currentVersion=520-19
latestVersion=520-19
if [[ "$latestVersion" > "$currentVersion" -o forceupdate ]]
then
echo -e "\nupdate!\n"
else
echo -e "\nno update!\n"
fi

$forceupdate inside brackets will actually be true, because it's not going to execute the false executable, but it will see a non-empty string.
if [[ "$latestVersion" > "$currentVersion" ]] || $forceupdate

Related

Bash substring test detecting empty string as a substring of a non-empty string

Consider this...
#!/bin/bash
declare STR='test'
declare SUB=''
[[ "${STR}" == *"${SUB}"* ]] && echo 'match'
This appears to resolve or evaluate to true? This makes no sense to me.
To get the expected result you have to test if the SUB string is empty?
#!/bin/bash
declare STR='test'
declare SUB=''
[[ ! -z "${SUB}" ]] && [[ "${STR}" == *"${SUB}"* ]] && echo 'match'
Is this some quirk with BASH or such? If the sub string is NOT in the string should it not return false?
The empty string is part of every string. Thus, testing whether an empty string is a substring of anything else is always true.
This is not specific to bash or in any way unusual; you can check it in other languages.
Python: print('' in 'foo') prints True.
Java: String("foo").contains("") returns true.
C: strstr("foo", "") returns a pointer to the first character in foo, meaning it found a match.
Ruby: "foo".include? "" returns true.
Yes, you should explicitly check for a possible empty string, but I'd use -n instead of a not, and in bash you can make it a single compound condition check.
$: x=abcdefg
$: y=cde
$: [[ -n "$y" && "$x" =~ $y ]] && echo match || echo no
match
$: y=
$: [[ -n "$y" && "$x" =~ $y ]] && echo match || echo no
no
...and you don't need the leading and trailing globs, they don't really add anything.

BASH OR statement

I want to execute echo only if one of the folders are not found?
However AEM_SEGMENTSTORE_LOCATION_AZURE is found but I still get "echo not found"
#!/bin/bash
AEM_SEGMENTSTORE_LOCATION="/opt/day/${AEM_RUNMODE}/crx-quickstart/repository/segmentstore"
AEM_SEGMENTSTORE_LOCATION_AZURE="/opt/day/crx-quickstart/repository/segmentstore"
[[ ! -d ${AEM_SEGMENTSTORE_LOCATION} ]] || [[ ! -d ${AEM_SEGMENTSTORE_LOCATION_AZURE} ]] && echo "not found"
In general, don't mix || and &&. The precedence is not what you expect. a || b && c is equivalent to (a || b) && c, but a && b || c is not the same as (a && b) || c. Get in the habit of using a proper if statement.
if [[ ! -d "$AEM_SEGMENTSTORE_LOCATION" || ! -d "$AEM_SEGMENTSTORE_LOCATION_AZURE" ]]; then
echo "not found"
fi
or
if ! [[ -d "$AEM_SEGMENTSTORE_LOCATION" && -d "$AEM_SEGMENTSTORE_LOCATION_AZURE" ]]; then
echo "not found"
fi
The precedence is wrong. You seem to be looking for AND anyway. You can easily fix this by changing it to
if [[ ! -d "$AEM_SEGMENTSTORE_LOCATION" ]] &&
[[ ! -d "$AEM_SEGMENTSTORE_LOCATION_AZURE" ]]
then
echo "$0: not found" >&2
fi
Notice also proper quoting of your variables {see When to wrap quotes around a shell variable; braces do not quote, and were basically useless here) and probably avoid uppercase variable names if these are private variables of yours (uppercase is reserved for system variables). Finally, the diagnostic message should probably go to standard error, and include the script's name; it should probably also say what was not found.

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.

bash - Possible to 'override' the test ([[)-builtin?

Is it possible to override Bash's test builtin? So that
[[ $1 = 'a' ]]
not just does the test but also outputs which result was expected when it fails? Something like
echo "Expected $1 to be a.'
EDIT
I know this is bad :-).
The test expression compound command does real short-circuiting that affects all expansions.
$ set -x
$ [[ 0 -gt x=1+1 || ++x -eq $(tee /dev/fd/3 <<<$x) && $(echo 'nope' >&3) ]] 3>&1
+ [[ 0 -gt x=1+1 ]]
++ tee /dev/fd/2
2
+ [[ ++x -eq 2 ]]
So yes you could do anything in a single test expression. In reality it's quite rare to have a test produce a side-effect, and almost never used to produce output.
Also yes, reserved words can be overridden. Bash is more lenient with ksh-style function definitions than POSIX style (which still allows some invalid names).
function [[ { [ "${#:1:${##}-1}" ]; }; \[[ -a -o -a -o -a ]] || echo lulz
Yet another forky bomb.
if function function if function if if \function & then \if & fi && \if & then \function & fi && then \function fi
Something like this?
if [[ $1 == 'a' ]]; then
echo "all right";
else
echo 'Expected $1 to be "a"'
fi
Anyway, what's the point of the test if you only expect one answer? Or do you mean that for debugging purposes?
[[ 'a' = 'a' ]] || echo "failed"
[[ 'b' = 'a' ]] || echo "failed"
failed

Escaping in test comparisons

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.)

Resources