How can I use a regex variable in zsh the same way it works in bash? I can only get zsh to work with an inline regex. I am just trying to test a string only contains alphanumerics, underscores or periods, but no dashes. As you can see, the inline regex and the regex variable work as expected in bash, but zsh only matches the inline regex.
Bash
#!/bin/bash
RE='[0-9A-Za-z_\.]'
for test in $#; do
echo -e "bash test: $test"
if [[ "${test//[0-9A-Za-z_\.]/}" = "" ]]; then
echo -e '\tmatch inline'
fi
if [[ "${test//$RE/}" = "" ]]; then
echo -e '\tmatch var'
fi
done
❯./bash-regex-test.sh foo_bar foo-bar
output:
bash test: foo_bar
match inline
match var
bash test: foo-bar
Zsh
#!/bin/zsh
RE='[0-9A-Za-z_\.]'
for test in $#; do
echo "zsh test: $test"
if [[ "${test//[0-9A-Za-z_\.]/}" = "" ]]; then
echo '\tmatch inline'
fi
if [[ "${test//$RE/}" = "" ]]; then
echo '\tmatch var'
fi
done
❯./zsh-regex-test.zsh foo_bar foo-bar
output:
zsh test: foo_bar
match inline
zsh test: foo-bar
With zsh you need to use ${~RE} instead of $RE so the variable $RE
is treated as a pattern, not the literal string. Then change the line as:
if [[ "${test//${~RE}/}" = "" ]]; then
BTW your usage of $RE is not the regex but the pattern as in
pathname expansion.
In order to use it as a regex, you'll need to use =~ operator as:
#!/bin/zsh
RE='^[0-9A-Za-z_\.]+$'
for test in "$#"; do
echo "zsh test: $test"
if [[ $test =~ ^[0-9A-Za-z_\.]+$ ]]; then
echo '\tmatch inline'
fi
if [[ $test =~ $RE ]]; then
echo '\tmatch var'
fi
done
Related
The following code
number=1
if [[ $number =~ [0-9] ]]
then
echo matched
fi
works. If I try to use quotes in the regex, however, it stops:
number=1
if [[ $number =~ "[0-9]" ]]
then
echo matched
fi
I tried "\[0-9\]", too. What am I missing?
Funnily enough, bash advanced scripting guide suggests this should work.
Bash version 3.2.39.
It was changed between 3.1 and 3.2. Guess the advanced guide needs an update.
This is a terse description of the new
features added to bash-3.2 since the
release of bash-3.1. As always, the
manual page (doc/bash.1) is the place
to look for complete descriptions.
New Features in Bash
snip
f. Quoting the string argument to the
[[ command's =~ operator now forces
string matching, as with the other pattern-matching operators.
Sadly this'll break existing quote using scripts unless you had the insight to store patterns in variables and use them instead of the regexes directly. Example below.
$ bash --version
GNU bash, version 3.2.39(1)-release (i486-pc-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
$ number=2
$ if [[ $number =~ "[0-9]" ]]; then echo match; fi
$ if [[ $number =~ [0-9] ]]; then echo match; fi
match
$ re="[0-9]"
$ if [[ $number =~ $re ]]; then echo MATCH; fi
MATCH
$ bash --version
GNU bash, version 3.00.0(1)-release (i586-suse-linux)
Copyright (C) 2004 Free Software Foundation, Inc.
$ number=2
$ if [[ $number =~ "[0-9]" ]]; then echo match; fi
match
$ if [[ "$number" =~ [0-9] ]]; then echo match; fi
match
Bash 3.2 introduced a compatibility option compat31 which reverts bash regular expression quoting behavior back to 3.1
Without compat31:
$ shopt -u compat31
$ shopt compat31
compat31 off
$ set -x
$ if [[ "9" =~ "[0-9]" ]]; then echo match; else echo no match; fi
+ [[ 9 =~ \[0-9] ]]
+ echo no match
no match
With compat31:
$ shopt -s compat31
+ shopt -s compat31
$ if [[ "9" =~ "[0-9]" ]]; then echo match; else echo no match; fi
+ [[ 9 =~ [0-9] ]]
+ echo match
match
Link to patch:
http://ftp.gnu.org/gnu/bash/bash-3.2-patches/bash32-039
GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)
Some examples of string match and regex match
$ if [[ 234 =~ "[0-9]" ]]; then echo matches; fi # string match
$
$ if [[ 234 =~ [0-9] ]]; then echo matches; fi # regex natch
matches
$ var="[0-9]"
$ if [[ 234 =~ $var ]]; then echo matches; fi # regex match
matches
$ if [[ 234 =~ "$var" ]]; then echo matches; fi # string match after substituting $var as [0-9]
$ if [[ 'rss$var919' =~ "$var" ]]; then echo matches; fi # string match after substituting $var as [0-9]
$ if [[ 'rss$var919' =~ $var ]]; then echo matches; fi # regex match after substituting $var as [0-9]
matches
$ if [[ "rss\$var919" =~ "$var" ]]; then echo matches; fi # string match won't work
$ if [[ "rss\\$var919" =~ "$var" ]]; then echo matches; fi # string match won't work
$ if [[ "rss'$var'""919" =~ "$var" ]]; then echo matches; fi # $var is substituted on LHS & RHS and then string match happens
matches
$ if [[ 'rss$var919' =~ "\$var" ]]; then echo matches; fi # string match !
matches
$ if [[ 'rss$var919' =~ "$var" ]]; then echo matches; fi # string match failed
$
$ if [[ 'rss$var919' =~ '$var' ]]; then echo matches; fi # string match
matches
$ echo $var
[0-9]
$
$ if [[ abc123def =~ "[0-9]" ]]; then echo matches; fi
$ if [[ abc123def =~ [0-9] ]]; then echo matches; fi
matches
$ if [[ 'rss$var919' =~ '$var' ]]; then echo matches; fi # string match due to single quotes on RHS $var matches $var
matches
$ if [[ 'rss$var919' =~ $var ]]; then echo matches; fi # Regex match
matches
$ if [[ 'rss$var' =~ $var ]]; then echo matches; fi # Above e.g. really is regex match and not string match
$
$ if [[ 'rss$var919[0-9]' =~ "$var" ]]; then echo matches; fi # string match RHS substituted and then matched
matches
$ if [[ 'rss$var919' =~ "'$var'" ]]; then echo matches; fi # trying to string match '$var' fails
$ if [[ '$var' =~ "'$var'" ]]; then echo matches; fi # string match still fails as single quotes are omitted on RHS
$ if [[ \'$var\' =~ "'$var'" ]]; then echo matches; fi # this string match works as single quotes are included now on RHS
matches
As mentioned in other answers, putting the regular expression in a variable is a general way to achieve compatibility over different bash versions. You may also use this workaround to achieve the same thing, while keeping your regular expression within the conditional expression:
$ number=1
$ if [[ $number =~ $(echo "[0-9]") ]]; then echo matched; fi
matched
$
Using a local variable has slightly better performance than using command substitution.
For larger scripts, or collections of scripts, it might make sense to use a utility to prevent unwanted local variables polluting the code, and to reduce verbosity. This seems to work well:
# Bash's built-in regular expression matching requires the regular expression
# to be unqouted (see https://stackoverflow.com/q/218156), which makes it harder
# to use some special characters, e.g., the dollar sign.
# This wrapper works around the issue by using a local variable, which means the
# quotes are not passed on to the regex engine.
regex_match() {
local string regex
string="${1?}"
regex="${2?}"
# shellcheck disable=SC2046 `regex` is deliberately unquoted, see above.
[[ "${string}" =~ ${regex} ]]
}
Example usage:
if regex_match "${number}" '[0-9]'; then
echo matched
fi
I have a value in a variable that may be absolute or relative url, and I need to check which one it is.
I have found that there's a =~ operator in [[, but I can't get it to work. What am I doing wrong?
url="http://test"
if [[ "$url" =~ "^http://" ]];
then echo "absolute.";
fi;
You need to use regex without quote:
url="http://test"
if [[ "$url" =~ ^http:// ]]; then
echo "absolute."
fi
This outputs `absolute. as regex needs to be without quote in newer BASH (after BASH v3.1)
Or avoid regex and use glob matching:
if [[ "$url" == "http://"* ]]; then
echo "absolute."
fi
I'm using Ubuntu system shell, not bash, and I found the regular way can not work:
#!/bin/sh
string='My string';
if [[ $string =~ .*My.* ]]
then
echo "It's there!"
fi
error [[: not found!
What can I do to solve this problem?
The [[ ... ]] are a bash-ism. You can make your test shell-agnostic by just using grep with a normal if:
if echo "$string" | grep -q "My"; then
echo "It's there!"
fi
Using grep for such a simple pattern can be considered wasteful. Avoid that unnecessary fork, by using the Sh built-in Glob-matching engine (NOTE: This does not support regex):
case "$value" in
*XXX*) echo OK ;;
*) echo fail ;;
esac
It is POSIX compliant. Bash have simplified syntax for this:
if [[ "$value" == *XXX* ]]; then :; fi
and even regex:
[[ abcd =~ b.*d ]] && echo ok
You could use expr:
if expr "$string" : "My" 1>/dev/null; then
echo "It's there";
fi
This would work with both sh and bash.
As a handy function:
exprq() {
local value
test "$2" = ":" && value="$3" || value="$2"
expr "$1" : "$value" 1>/dev/null
}
# Or `exprq "somebody" "body"` if you'd rather ditch the ':'
if exprq "somebody" : "body"; then
echo "once told me"
fi
Quoting from man expr:
STRING : REGEXP
anchored pattern match of REGEXP in STRING
How to validate whether the given input string is valid Hex value or not using regex in shell scripts
For example:
Input var="ff:ff:fe:ff"
There is a : deliminator value
I want to use this regex for any input String
var = "ff:ff:fe:ff:fe"
var = "ff:ff:fe:ff:fe:fe:ff:ff"
\b0[xX][0-9a-fA-F]+\b
#!/bin/bash -x
var="fe:fe:fe:fe"
regex="/^([0-9A-F]+:?){4}$/"
if [[ $var =~ $regex ]]; then
echo "valid"
fi
Better version (thanks to chepner):
^([[:xdigit:]]{2})(:[[:xdigit:]]{2})*$
Test
if [[ "ff:af:ff:23:a2:ad" =~ ^([[:xdigit:]]{2})(:[[:xdigit:]]{2})*$ ]]; then
echo "match";
fi
Old Answer:
^([0-9A-Fa-f]{2})(:[0-9A-Fa-f]{2})*$
Test
$ if [[ "ff:af:ff:23:a2:ad" =~ ^([0-9A-Fa-f]{2})(:[0-9A-Fa-f]{2})*$ ]]; then
echo "match";
fi
$ match
$ if [[ "definitlynottherightformat" =~ ^([0-9A-Fa-f]{2})(:[0-9A-Fa-f]{2})*$ ]]; then
echo "match";
fi
$
The following code
number=1
if [[ $number =~ [0-9] ]]
then
echo matched
fi
works. If I try to use quotes in the regex, however, it stops:
number=1
if [[ $number =~ "[0-9]" ]]
then
echo matched
fi
I tried "\[0-9\]", too. What am I missing?
Funnily enough, bash advanced scripting guide suggests this should work.
Bash version 3.2.39.
It was changed between 3.1 and 3.2. Guess the advanced guide needs an update.
This is a terse description of the new
features added to bash-3.2 since the
release of bash-3.1. As always, the
manual page (doc/bash.1) is the place
to look for complete descriptions.
New Features in Bash
snip
f. Quoting the string argument to the
[[ command's =~ operator now forces
string matching, as with the other pattern-matching operators.
Sadly this'll break existing quote using scripts unless you had the insight to store patterns in variables and use them instead of the regexes directly. Example below.
$ bash --version
GNU bash, version 3.2.39(1)-release (i486-pc-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
$ number=2
$ if [[ $number =~ "[0-9]" ]]; then echo match; fi
$ if [[ $number =~ [0-9] ]]; then echo match; fi
match
$ re="[0-9]"
$ if [[ $number =~ $re ]]; then echo MATCH; fi
MATCH
$ bash --version
GNU bash, version 3.00.0(1)-release (i586-suse-linux)
Copyright (C) 2004 Free Software Foundation, Inc.
$ number=2
$ if [[ $number =~ "[0-9]" ]]; then echo match; fi
match
$ if [[ "$number" =~ [0-9] ]]; then echo match; fi
match
Bash 3.2 introduced a compatibility option compat31 which reverts bash regular expression quoting behavior back to 3.1
Without compat31:
$ shopt -u compat31
$ shopt compat31
compat31 off
$ set -x
$ if [[ "9" =~ "[0-9]" ]]; then echo match; else echo no match; fi
+ [[ 9 =~ \[0-9] ]]
+ echo no match
no match
With compat31:
$ shopt -s compat31
+ shopt -s compat31
$ if [[ "9" =~ "[0-9]" ]]; then echo match; else echo no match; fi
+ [[ 9 =~ [0-9] ]]
+ echo match
match
Link to patch:
http://ftp.gnu.org/gnu/bash/bash-3.2-patches/bash32-039
GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)
Some examples of string match and regex match
$ if [[ 234 =~ "[0-9]" ]]; then echo matches; fi # string match
$
$ if [[ 234 =~ [0-9] ]]; then echo matches; fi # regex natch
matches
$ var="[0-9]"
$ if [[ 234 =~ $var ]]; then echo matches; fi # regex match
matches
$ if [[ 234 =~ "$var" ]]; then echo matches; fi # string match after substituting $var as [0-9]
$ if [[ 'rss$var919' =~ "$var" ]]; then echo matches; fi # string match after substituting $var as [0-9]
$ if [[ 'rss$var919' =~ $var ]]; then echo matches; fi # regex match after substituting $var as [0-9]
matches
$ if [[ "rss\$var919" =~ "$var" ]]; then echo matches; fi # string match won't work
$ if [[ "rss\\$var919" =~ "$var" ]]; then echo matches; fi # string match won't work
$ if [[ "rss'$var'""919" =~ "$var" ]]; then echo matches; fi # $var is substituted on LHS & RHS and then string match happens
matches
$ if [[ 'rss$var919' =~ "\$var" ]]; then echo matches; fi # string match !
matches
$ if [[ 'rss$var919' =~ "$var" ]]; then echo matches; fi # string match failed
$
$ if [[ 'rss$var919' =~ '$var' ]]; then echo matches; fi # string match
matches
$ echo $var
[0-9]
$
$ if [[ abc123def =~ "[0-9]" ]]; then echo matches; fi
$ if [[ abc123def =~ [0-9] ]]; then echo matches; fi
matches
$ if [[ 'rss$var919' =~ '$var' ]]; then echo matches; fi # string match due to single quotes on RHS $var matches $var
matches
$ if [[ 'rss$var919' =~ $var ]]; then echo matches; fi # Regex match
matches
$ if [[ 'rss$var' =~ $var ]]; then echo matches; fi # Above e.g. really is regex match and not string match
$
$ if [[ 'rss$var919[0-9]' =~ "$var" ]]; then echo matches; fi # string match RHS substituted and then matched
matches
$ if [[ 'rss$var919' =~ "'$var'" ]]; then echo matches; fi # trying to string match '$var' fails
$ if [[ '$var' =~ "'$var'" ]]; then echo matches; fi # string match still fails as single quotes are omitted on RHS
$ if [[ \'$var\' =~ "'$var'" ]]; then echo matches; fi # this string match works as single quotes are included now on RHS
matches
As mentioned in other answers, putting the regular expression in a variable is a general way to achieve compatibility over different bash versions. You may also use this workaround to achieve the same thing, while keeping your regular expression within the conditional expression:
$ number=1
$ if [[ $number =~ $(echo "[0-9]") ]]; then echo matched; fi
matched
$
Using a local variable has slightly better performance than using command substitution.
For larger scripts, or collections of scripts, it might make sense to use a utility to prevent unwanted local variables polluting the code, and to reduce verbosity. This seems to work well:
# Bash's built-in regular expression matching requires the regular expression
# to be unqouted (see https://stackoverflow.com/q/218156), which makes it harder
# to use some special characters, e.g., the dollar sign.
# This wrapper works around the issue by using a local variable, which means the
# quotes are not passed on to the regex engine.
regex_match() {
local string regex
string="${1?}"
regex="${2?}"
# shellcheck disable=SC2046 `regex` is deliberately unquoted, see above.
[[ "${string}" =~ ${regex} ]]
}
Example usage:
if regex_match "${number}" '[0-9]'; then
echo matched
fi