I was trying to check if an array contained a value and I found Check if a Bash array contains a value
It did not work for me, and it was because I decided to remove the spaces surrounding the quotes, like this:
words=(aa bb cc)
[[ " ${words[#]} " =~ " a " ]] && echo "YES" || echo "NO"; # This is the real answer, and it works
------
[[ "${words[#]}" =~ "a" ]] && echo "YES" || echo "NO"; # This does not work. Why???
Whats the difference when you surround them with spaces or when you dont?
And for my curiosity. In the previous question I mentioned, some answers/comments go with:
${array[*]}, and others with ${array[#]}
Do they both "iterate" through the loop in the same way?
[[ string =~ regex ]] checks if regex matches any substring of string. That operator does not iterate over your array entries as for entry in "${words[#]}" would do. It cannot even handle arrays.
[[ " ${words[#]} " =~ " a " ]] is only a hack for finding array elements. The array is converted into a single string where all array entries are surrounded by spaces. Then, we search for an element using a string search. This only works reliably, if the array entries themselves do not contain any spaces.
For the difference between ${array[*]} and ${array[#]} see bash's manual:
If the word is double-quoted, ${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS variable [= a space by default], and ${name[#]} expands each element of name to a separate word.
So in this case, * would be the logical way to write [[ " ${words[*]} " =~ " a " ]], because it is equivalent to [[ " aa bb cc " =~ " a " ]].
That # works here too is somewhat strange, because [[ " aa" "bb" "cc " =~ " a " ]] gives a syntax error. Bash tolerating the misused # in [[ is probably an undocumented "feature" related to the (documented) disabled word-splitting inside [[. For the basic POSIX test command [ the difference between * and # works as expected, meaning a=("x" "=" "y"); [ "${a[#]}" ] fails because x != y.
Related
This question already has answers here:
How to compare strings in Bash
(12 answers)
Closed 4 years ago.
I'm trying to get an if statement to work in Bash (using Ubuntu):
#!/bin/bash
s1="hi"
s2="hi"
if ["$s1" == "$s2"]
then
echo match
fi
I've tried various forms of the if statement, using [["$s1" == "$s2"]], with and without quotes, using =, == and -eq, but I still get the following error:
[hi: command not found
I've looked at various sites and tutorials and copied those, but it doesn't work - what am I doing wrong?
Eventually, I want to say if $s1 contains $s2, so how can I do that?
I did just work out the spaces bit... :/ How do I say contains?
I tried
if [[ "$s1" == "*$s2*" ]]
but it didn't work.
For string equality comparison, use:
if [[ "$s1" == "$s2" ]]
For string does NOT equal comparison, use:
if [[ "$s1" != "$s2" ]]
For the a contains b, use:
if [[ $s1 == *"$s2"* ]]
(and make sure to add spaces between the symbols):
Bad:
if [["$s1" == "$s2"]]
Good:
if [[ "$s1" == "$s2" ]]
You should be careful to leave a space between the sign of '[' and double quotes where the variable contains this:
if [ "$s1" == "$s2" ]; then
# ^ ^ ^ ^
echo match
fi
The ^s show the blank spaces you need to leave.
You need spaces:
if [ "$s1" == "$s2" ]
I suggest this one:
if [ "$a" = "$b" ]
Notice the white space between the openning/closing brackets and the variables and also the white spaces wrapping the '=' sign.
Also, be careful of your script header. It's not the same thing whether you use
#!/bin/bash
or
#!/bin/sh
Here's the source.
Bash 4+ examples. Note: not using quotes will cause issues when words contain spaces, etc. Always quote in Bash IMO.
Here are some examples Bash 4+:
Example 1, check for 'yes' in string (case insensitive):
if [[ "${str,,}" == *"yes"* ]] ;then
Example 2, check for 'yes' in string (case insensitive):
if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then
Example 3, check for 'yes' in string (case sensitive):
if [[ "${str}" == *"yes"* ]] ;then
Example 4, check for 'yes' in string (case sensitive):
if [[ "${str}" =~ "yes" ]] ;then
Example 5, exact match (case sensitive):
if [[ "${str}" == "yes" ]] ;then
Example 6, exact match (case insensitive):
if [[ "${str,,}" == "yes" ]] ;then
Example 7, exact match:
if [ "$a" = "$b" ] ;then
This question has already great answers, but here it appears that there is a slight confusion between using single equal (=) and double equals (==) in
if [ "$s1" == "$s2" ]
The main difference lies in which scripting language you are using. If you are using Bash then include #!/bin/bash in the starting of the script and save your script as filename.bash. To execute, use bash filename.bash - then you have to use ==.
If you are using sh then use #!/bin/sh and save your script as filename.sh. To execute use sh filename.sh - then you have to use single =. Avoid intermixing them.
I would suggest:
#!/bin/bash
s1="hi"
s2="hi"
if [ $s1 = $s2 ]
then
echo match
fi
Without the double quotes and with only one equals.
$ if [ "$s1" == "$s2" ]; then echo match; fi
match
$ test "s1" = "s2" ;echo match
match
$
I don't have access to a Linux box right now, but [ is actually a program (and a Bash builtin), so I think you have to put a space between [ and the first parameter.
Also note that the string equality operator seems to be a single =.
This is more a clarification than an answer! Yes, the clue is in the error message:
[hi: command not found
which shows you that your "hi" has been concatenated to the "[".
Unlike in more traditional programming languages, in Bash, "[" is a command just like the more obvious "ls", etc. - it's not treated specially just because it's a symbol, hence the "[" and the (substituted) "$s1" which are immediately next to each other in your question, are joined (as is correct for Bash), and it then tries to find a command in that position: [hi - which is unknown to Bash.
In C and some other languages, the "[" would be seen as a different "character class" and would be disjoint from the following "hi".
Hence you require a space after the opening "[".
Use:
#!/bin/bash
s1="hi"
s2="hi"
if [ "x$s1" == "x$s2" ]
then
echo match
fi
Adding an additional string inside makes it more safe.
You could also use another notation for single-line commands:
[ "x$s1" == "x$s2" ] && echo match
For a version with pure Bash and without test, but really ugly, try:
if ( exit "${s1/*$s2*/0}" )2>/dev/null
then
echo match
fi
Explanation: In ( )an extra subshell is opened. It exits with 0 if there was a match, and it tries to exit with $s1 if there was no match which raises an error (ugly). This error is directed to /dev/null.
How to check in bash that string contains only blank characters as space, tab, new line? I'm trying this but it doesn't work:
if [[ "$1" == #([\t\n ]) ]]; then
echo "Empty"
fi
Using a regular expression:
if [[ $1 =~ ^[[:space:]]+$ ]]; then
echo "Only whitespace"
else
echo "There are non-whitespace characters."
fi
Use * instead of + if you also want to match empty strings.
Convert it to an array, it will be empty if string contains only blank characters
string=" "
array=($string)
[[ ${!array[#]} ]] && echo fail || echo ok
I have a string: dev/2.0 or dev/2.0-tymlez. How can I extract the string after the last - hyphen in bash? If there is no -, then the variable should be empty else tymlez and I want to store the result in $STRING. After that I would like to check the variable with:
if [ -z "$STRING" ]
then
echo "\$STRING is empty"
else
echo "\$STRING is NOT empty"
fi
Is that possible?
I recommend against calling your variable STRING. All-uppercase variables are used by the system (e.g. HOME) or the shell itself (e.g. PWD, RANDOM).
That said, you could do something like
string='dev/2.0-tymlez'
case "$string" in
*-*) string="${string##*-}";;
*) string='';;
esac
It's a bit clunky: It first checks whether there are any - at all, and if so, it removes the longest prefix matching *-; otherwise it just sets string to empty (because *- wouldn't have matched anything then).
You could use the =~ operator:
string="dev/2.0-tymlez"
[[ $string =~ -([^-]+)$ ]]; string=${BASH_REMATCH[1]}
BASH_REMATCH is a special array where the matches from [[ ... =~ ... ]] are assigned to.
You can use sed:
for string in "dev/2.0" "dev/2.0-1-2-3" "dev/2.0-tymlez"; do
string=$(sed 's/[^-]*[-]*//' <<< "${string}")
echo "string=[${string}]"
done
Result
string=[]
string=[1-2-3]
string=[tymlez]
This question already has answers here:
How to compare strings in Bash
(12 answers)
Closed 4 years ago.
I'm trying to get an if statement to work in Bash (using Ubuntu):
#!/bin/bash
s1="hi"
s2="hi"
if ["$s1" == "$s2"]
then
echo match
fi
I've tried various forms of the if statement, using [["$s1" == "$s2"]], with and without quotes, using =, == and -eq, but I still get the following error:
[hi: command not found
I've looked at various sites and tutorials and copied those, but it doesn't work - what am I doing wrong?
Eventually, I want to say if $s1 contains $s2, so how can I do that?
I did just work out the spaces bit... :/ How do I say contains?
I tried
if [[ "$s1" == "*$s2*" ]]
but it didn't work.
For string equality comparison, use:
if [[ "$s1" == "$s2" ]]
For string does NOT equal comparison, use:
if [[ "$s1" != "$s2" ]]
For the a contains b, use:
if [[ $s1 == *"$s2"* ]]
(and make sure to add spaces between the symbols):
Bad:
if [["$s1" == "$s2"]]
Good:
if [[ "$s1" == "$s2" ]]
You should be careful to leave a space between the sign of '[' and double quotes where the variable contains this:
if [ "$s1" == "$s2" ]; then
# ^ ^ ^ ^
echo match
fi
The ^s show the blank spaces you need to leave.
You need spaces:
if [ "$s1" == "$s2" ]
I suggest this one:
if [ "$a" = "$b" ]
Notice the white space between the openning/closing brackets and the variables and also the white spaces wrapping the '=' sign.
Also, be careful of your script header. It's not the same thing whether you use
#!/bin/bash
or
#!/bin/sh
Here's the source.
Bash 4+ examples. Note: not using quotes will cause issues when words contain spaces, etc. Always quote in Bash IMO.
Here are some examples Bash 4+:
Example 1, check for 'yes' in string (case insensitive):
if [[ "${str,,}" == *"yes"* ]] ;then
Example 2, check for 'yes' in string (case insensitive):
if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then
Example 3, check for 'yes' in string (case sensitive):
if [[ "${str}" == *"yes"* ]] ;then
Example 4, check for 'yes' in string (case sensitive):
if [[ "${str}" =~ "yes" ]] ;then
Example 5, exact match (case sensitive):
if [[ "${str}" == "yes" ]] ;then
Example 6, exact match (case insensitive):
if [[ "${str,,}" == "yes" ]] ;then
Example 7, exact match:
if [ "$a" = "$b" ] ;then
This question has already great answers, but here it appears that there is a slight confusion between using single equal (=) and double equals (==) in
if [ "$s1" == "$s2" ]
The main difference lies in which scripting language you are using. If you are using Bash then include #!/bin/bash in the starting of the script and save your script as filename.bash. To execute, use bash filename.bash - then you have to use ==.
If you are using sh then use #!/bin/sh and save your script as filename.sh. To execute use sh filename.sh - then you have to use single =. Avoid intermixing them.
I would suggest:
#!/bin/bash
s1="hi"
s2="hi"
if [ $s1 = $s2 ]
then
echo match
fi
Without the double quotes and with only one equals.
$ if [ "$s1" == "$s2" ]; then echo match; fi
match
$ test "s1" = "s2" ;echo match
match
$
I don't have access to a Linux box right now, but [ is actually a program (and a Bash builtin), so I think you have to put a space between [ and the first parameter.
Also note that the string equality operator seems to be a single =.
This is more a clarification than an answer! Yes, the clue is in the error message:
[hi: command not found
which shows you that your "hi" has been concatenated to the "[".
Unlike in more traditional programming languages, in Bash, "[" is a command just like the more obvious "ls", etc. - it's not treated specially just because it's a symbol, hence the "[" and the (substituted) "$s1" which are immediately next to each other in your question, are joined (as is correct for Bash), and it then tries to find a command in that position: [hi - which is unknown to Bash.
In C and some other languages, the "[" would be seen as a different "character class" and would be disjoint from the following "hi".
Hence you require a space after the opening "[".
Use:
#!/bin/bash
s1="hi"
s2="hi"
if [ "x$s1" == "x$s2" ]
then
echo match
fi
Adding an additional string inside makes it more safe.
You could also use another notation for single-line commands:
[ "x$s1" == "x$s2" ] && echo match
For a version with pure Bash and without test, but really ugly, try:
if ( exit "${s1/*$s2*/0}" )2>/dev/null
then
echo match
fi
Explanation: In ( )an extra subshell is opened. It exits with 0 if there was a match, and it tries to exit with $s1 if there was no match which raises an error (ugly). This error is directed to /dev/null.
Curious to know why the following is not working for character: +
Characters "\", "(" and "*" makes sense i.e. * will expand to folders/files in current directory (during command line shell expansion) and similarly \ and ( will expect closing character to work but my understanding was "+" should have worked like "-" did.
PS: I know putting double quotes i.e. "${o}" in the IF statement, will work for all characters in my test case below. Using \${o} in IF statement with or without double quote will fail all the checks.
$ for o in - + \` ~ \~ , _ = / \\ ! # \# $ \$ % ^ \& \* \( \); do a="a${o}b${o}c";if [[ $a =~ ${o} ]]; then echo "${o} exists in $a and =~ works"; else echo -e "\ncharacter ${o} doesn't work with =~\n"; fi; done
- exists in a-b-c and =~ works
character + doesn't work with =~
` exists in a`b`c and =~ works
/home/ubuntu exists in a/home/ubuntub/home/ubuntuc and =~ works
~ exists in a~b~c and =~ works
, exists in a,b,c and =~ works
_ exists in a_b_c and =~ works
= exists in a=b=c and =~ works
/ exists in a/b/c and =~ works
character \ doesn't work with =~
! exists in a!b!c and =~ works
# exists in a#b#c and =~ works
# exists in a#b#c and =~ works
$ exists in a$b$c and =~ works
$ exists in a$b$c and =~ works
% exists in a%b%c and =~ works
^ exists in a^b^c and =~ works
& exists in a&b&c and =~ works
character * doesn't work with =~
character ( doesn't work with =~
) exists in a)b)c and =~ works
The fundamental misunderstanding behind this question is that =~ is a substring-search operator. It is not.
The right-hand side of =~ is evaluated as a POSIX ERE expression. =~ is thus a regex-matching operator, which happens to be frequently used for searches when the right-hand side is quoted to make its contents literal (or when this string is known to match only itself when interpreted as an ERE).
+, in regex, means "1-or-more of the preceding token" -- just as * means "0-or-more of the preceding token".
Thus, either [[ $foo =~ + ]] or [[ $foo =~ * ]] makes no sense, because these are checking for zero-or-more of a preceding token that doesn't exist at all.
Similarly, ( and ) have meaning in ERE as the beginning and end of a match group, so when they're given bare (unescaped/unquoted), they result in an invalid regex.
If you quote the expansion, by contrast, all characters contained will be treated as literal, rather than being treated as regular expression metacharacters, thus resulting in the presumably-intended behavior.
If you want to check whether a literal character is contained in a string, either quote it -- [[ $foo =~ "$o" ]] -- or use a glob-style pattern: [[ $foo = *"$o"* ]]