Why "if [[ ${value} == ^[[:blank:]]*$ ]]" cannot detect empty variable? - bash

I tried the following three codes to detects empty variable.
unset value
if [[ -z ${value} ]]; then
echo "value is judged as empty by [[ -z ]] : value = '${value}'"
fi
if [[ ${value} == ^[[:blank:]]*$ ]]; then
echo "value is judged as empty by [[ == ]] : value = '${value}'"
fi
if echo ${value} | grep -E "^[[:blank:]]*$"; then
echo "value is judged as empty by grep -E "^[[:blank:]]*$" : value = '${value}'"
fi
The results are following,
value is judged as empty by [[ -z ]] : value = ''
value is judged as empty by grep -E ^[[:blank:]]* : value = ''
Why the second one does not work? Please let me know.

Your second one is a pattern, not a regular expression. It only matches strings with at least 3 characters: ^, a single blank character, zero or more arbitrary characters, and $. To match the regular expression representing zero or more blank characters, use the =~ operator.
if [[ $value =~ ^[[:blank:]]*$ ]]; then

Related

how to check strings first char in bash

I want to check if a string's first char is uppercase, lowercase or anything else. I tried this code but I can't get to the last else although the first two conditions are false.
#!/bin/bash
echo "enter var: "
read var
if [[ {$var::1 =~ [A-Z] ]]
then
echo "UpperCase"
elif [[ {$var::1} =~ [a-z] ]]
then
echo "LowerCase"
else
echo "Digit or a symbol"
fi
exit
When I enter 1hello I get: "LowerCase"
What am I missing here?!
You don't necessarily need to extract the first character, you can compare the whole string to a pattern.
Here, I'm using the POSIX character classes [:upper:] and [:lower:] which I find more descriptive. They also handle non-ASCII letters.
Regex matching:
if [[ $var =~ ^[[:upper:]] ]]; then echo starts with an upper
elif [[ $var =~ ^[[:lower:]] ]]; then echo starts with a lower
else echo does not start with a letter
fi
With shell glob patterns -- within [[...]] the == operator does pattern matching not just string equality
if [[ $var == [[:upper:]]* ]]; then echo starts with an upper
elif [[ $var == [[:lower:]]* ]]; then echo starts with a lower
else echo does not start with a letter
fi
A case statement would work here as well
case "$var" in
[[:upper:]]*) echo starts with an upper ;;
[[:lower:]]*) echo starts with a lower ;;
*) echo does not start with a letter ;;
esac
Neither of your parameter expansions are correct. {$var::1 evaluates to {1hello::1, not 1, and {$var::1} likewise evaluates to {1hello::1}.
The expansion you want is ${var::1}, which does expand to 1 as intended.
You don't need a fancy parameter expansion anyway; you can match against the first character using regular expressions alone
[[ $var =~ ^[a-z] ]]
or pattern-matching
[[ $var = [a-z]* ]]
Regular expressions are not implicitly anchored, so you can use ^ to explicitly match the beginning of the string; the remainder of the string can be ignored.
Pattern matches are implicitly anchored to the start and end of the string, so you need * to match everything (if anything) that follows the initial character of the string.

How to test whether a string doesn't contain specified letters in bash

In a program I am writing I need to check whether a string contains characters not in another string e.g.:
if [ $string ?? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ]; then
echo $string isnt alphanumeric
fi
or
if [ $string ?? "ABCDEGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"]; then
echo $string isnt alphabetic
fi
Where ?? is the mystery operation.
Any help would be appreciated!
In bash, within [[...]], the == and != operators are pattern matching operators, so
validChars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
if [[ $string != *["$validChars"]* ]]; then
echo "$string does NOT contain any of $validChars"
fi
Ref: https://www.gnu.org/software/bash/manual/bash.html#index-_005b_005b
To test that a string ONLY contains characters in $validChars, we can use bash's extended pattern matching
if [[ $string == +(["$validChars"]) ]]; then
echo "$string ONLY contains chars in $validChars"
fi
if [[ $string != +(["$validChars"]) ]]; then
echo "string is empty or contains some character not in $validChars"
fi

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

Check if string contains non digit characters

How can I check if a given string contains non numeric characters, examples :
x11z returns > 0
x$1 also returns > 0
1111~ also returns > 0
By character I mean everything not between 0-9. I saw similar threads but non of them talks about "non 0-9" except they show if its a-z or A-Z.
Just use a negated character class:
grep [^0-9]
This will match any non-numeric character, and not strings composed of only digits.
Just by using bash pattern matching:
[[ "$MY_VAR" =~ ^[^0-9]+$ ]] && echo "no digit in $MY_VAR"
Something like this:
if [[ "xf44wd" =~ [0-9]+ ]]; then
echo "contains $?"
else
echo "does no contains $?"
fi
or
if [[ ! "xf44wd" =~ [0-9]+ ]]; then
echo "does not contains $?"
else
echo "contains $?"
fi
Most of these suggestions return true if the first character is a digit, but don't find errors within the string. The function below returns true if the entire string is digits, false if any non-digits are detected in the string.
function isdigit () {
[ $# -eq 1 ] || return 1;
[[ $1 = *[^0-9]* ]] && return 1
return 0
}
Another option, a bash "containment" check
[[ "xf4fgh" = *[^0-9]* ]]
echo $?
0
[[ "1234" = *[^0-9]* ]]
echo $?
1

Bash, if's, reusing variables

if [[ $line == *"option 1"* ]]
then
CURRENT_OPTION=1
fi
if [[ $line == *"option 2"* ]]
then
CURRENT_OPTION=2
fi
if [[ $line =~ "What i want" ]]
then
if [[ $CURRENT_OPTION -eq 1 ]]
then
MEM1=$(awk '/Used heap/ { gsub(/M/, " "); print $4 }')
elif [[ $CURRENT_OPTION -eq 2 ]]
then
MEM2=$(awk '/Used heap/ { gsub(/M/, " "); print $4 }')
fi
fi
Because CURRENT_OPTION is defined within an if, its value is not correct when checked in the third if. How do I pass it out so that it is?
Just declare CURRENT_OPTION at the top, something like:
declare -i CURRENT_OPTION=0
i to declare it as an int.
In all of your if statements you should enclose the variables in double quotes. If the variable is an empty string (or if the variable doesn't exist) then the if statement will not contain enough arguments and will throw an error.
Here is an example:
if [[ $var -eq 1 ]]
then
echo yes
else
echo no
fi
If var is uninitialised, bash will expand the statement to look like this:
if [[ -eq 1 ]]
then
echo yes
else
echo no
fi
There are not enough arguments to make the if statement valid here, and bash will throw an error:
bash: conditional binary operator expected
bash: syntax error near `1'
By wrapping the variable in quotes, this situation is avoided. This statement:
if [[ "$var" -eq 1 ]]
...
is expanded to:
if [[ "" -eq 1 ]]
...
and now the if statement has enough arguments (the first one being an empty string) to parse.

Resources