I'm curious why this code does not match -- take the "then" branch. It echos "no match" Can you please advise?
#!/bin/bash
suffix="2"
if [[ $suffix =~ "^[0-9]+$" ]]
then
echo "match"
else
echo "no match"
fi
Quoting the right-hand side of an = or =~ operation within [[ ]] in modern (3.2+) releases of bash makes the string literal -- ie. no longer a regex or pattern.
From the manual:
Any part of the pattern may be quoted to force the quoted portion to be matched as a string.
For consistent behavior across releases supporting =~ (if one needs to support versions prior to 3.2), accepted best practice is to put your regex in a variable, and use that variable unquoted on the right-hand side of =~:
re='^[0-9]+$'
[[ $suffix =~ $re ]]
Related
luajit -v
LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/
I want to negate match the version part, and got LUAJIT_VERSION="2.1.0-beta3" at the beginning of bash script. I use:
if ! [[ "$(luajit -v)" =~ LuaJIT\s+"$LUAJIT_VERSION".* ]]; then
#rest of code
But it seems not working whether I put $LUAJIT_VERSION between "" or not:
Any part of the pattern may be quoted to force the quoted portion to be matched as a string ... If the pattern is stored in a shell variable, quoting the variable expansion forces the entire pattern to be matched as a string.
Bash docs
Can you tell me what's the correct way to do this task?
\s is not a recognized character class in bash; you need to use [[:blank:]] instead:
if ! [[ "$(luajit -v)" =~ LuaJIT[[:blank:]]+"$LUAJIT_VERSION" ]]; then
(The trailing .* isn't necessary, since regular expressions aren't anchored to the start or end of the string.)
However, it's not clear your regular expression needs to be that general. It looks like you can use a single, literal space
if ! [[ "$(luajit -v)" =~ LuaJIT\ "$LUAJIT_VERSION" ]];
or simply use pattern matching:
if [[ "$(luajit -v)" != LuaJIT\ "$LUAJIT_VERSION"* ]];
This question already has answers here:
Bash Regular Expression -- Can't seem to match any of \s \S \d \D \w \W etc
(6 answers)
Closed 8 months ago.
If I pass a word as an argument by:
$./file.sh hello
it gives Even as output when it should print "Argument should be a number"
#!/bin/bash
set -e
if [[ -z $1 ]]
then
echo "Argument expected"
else
if [[ $1 =~ "\D" ]] #This does not work as expected
then
echo "Argument should be a number"
else
a=$1%2
if [[ a -eq 0 ]]
then
echo "Even"
elif [[ a -eq 1 ]]
then
echo "Odd"
fi
fi
fi
#End of program
When I change "\D" to "[^0-9]" in the if statement, it works as expected and prints "Argument should be a number" to the console.
Don't they both have the same meaning? If not, in what way are the two different from each other?
Bash uses POSIX Extended Regular Expressions, not PCRE. Instead of escape sequences like \D, it uses Bracket Expressions. The bracket expression for digits is
[:digit:]
and to match non-digits, you use this inside a character class with the negation operator:
[^[:digit:]]
As you can see, this is longer than just writing [^0-9], so it's not really a shorthand. It's useful for portability to other locales, since it will include their digits as well.
Bash regex simply does not support PCRE regex syntax.
You might want to read up on different regex dialects and their history.
See e.g. Why are there so many different regular expression dialects?
The POSIX equivalent of \D is [^[:digit:]].
Found this expression in some shell scripts. What does the double-curly brace star "{{*" mean?
if [[ "$_DIR" = {{* ]]
I've looked through http://www.gnu.org/software/bash/manual/bash.html and tried searching for it, but can't find an explanation.
This means: "if $_DIR" expands to a string starting with "{{"...
Thus, {{* doesn't actually mean anything to bash at all, more than if you wrote
for file in {{*; do printf '%s\n' "$file"; done
...would mean that {{* had any more meaning to bash than "all files whose names start with two curly braces".
Search for "pattern matching" in BashFAQ #31 for more on [[ and how it's extended to offer functionality not present in test, including both glob-style pattern matching and native regular expression support.
Alternately, look for [[…]] in the Conditional Constructs section of the bash manual.
Incidentally, the POSIX sh equivalent to this would be:
case $_DIR in
{{*) echo "put the true branch here" ;;
*) echo "put the false branch here" ;;
esac
Bash's manpage teaches that [[ == ]] matches patterns. In Bash therefore, why does the following not print matched?
Z=abc; [[ "$Z" == 'a*' ]] && echo 'matched'
The following however does indeed print matched:
Z=abc; [[ "$Z" == a* ]] && echo 'matched'
Isn't this exactly backward? Why does the a*, without the quotes, not immediately expand to list whatever filenames happen to begin with the letter a in the current directory? And besides, why doesn't the quoted 'a*' work in any case?
Glob pattern must not be quoted to make it work.
This should also work with just glob pattern out of quote whereas static text is still qupted:
[[ "$Z" == "a"* ]] && echo 'matched'
matched
[[ "$Z" == "ab"* ]] && echo 'matched'
matched
Explanation snippet from man page:
When the == and != operators are used, the string to the right of
the operator is considered a pattern and matched according to the
rules described below under Pattern Matching. If the shell option
nocasematch is enabled, the match is performed without regard to
the case of alphabetic characters. The return value is 0 if the
string matches (==) or does not match (!=) the pattern, and 1
otherwise. Any part of the pattern may be quoted to force it to be
matched as a string.
Additionally, one of the reasons to use [[ over [ is that [[ is a shell built-in and thus can have its own syntax and doesn't need to follow the normal expansion rules (which is why the arguments to [[ aren't subject to word-splitting for example).
While the existing answer is correct, I don't believe that it tells the full story.
Globs have two uses. There is a difference in behaviour between globs inside a [[ ]] construct which test the contents of a variable against a pattern and other globs, which expand to list a range of files. In either case, if you put quotes around character, it will be interpreted literally and not expanded.
It is also worth mentioning that the variable on the left hand side doesn't need to be quoted after the [[, so you could write your code like this:
Z=abc; [[ $Z == a* ]] && echo 'matched'
It is also possible to use a single = but the == looks more familiar to those coming from other coding backgrounds, so personally I prefer to use it in bash as well. As mentioned in the comments, the single = is the more widely compatible, as it is used to test string equality in all of POSIX-compliant shells, e.g. [ "$a" = "abc" ]. For this reason you may prefer to use it in bash as well.
As always, Greg's wiki contains some good information on the subject of pattern matching in bash.
The other day I was struggling with an if statement. Turns our my variable had a white space at the beginning. So I tried to conquer this with the following code but I am having no luck.
if [ "$COMMAND_WAIT" == "*REBOOT" ]; then
sudo /etc/kca/scripts/reboot.sh
echo "REBOOTING"
fi
Should I be able to wildcard this statement or is there another way around this?
The following should work. It uses [[ instead of [, and no quotes around the pattern.
if [[ "$COMMAND_WAIT" == *REBOOT ]]; then
sudo /etc/kca/scripts/reboot.sh
echo "REBOOTING"
fi
[[ expression ]] is a compound expression, with special rules regarding expansions and quoting. In contrast, [ is a builtin command, i.e. *REBOOT will be expanded as a pathname. In most cases, it's easier to use [[ instead of [.