Just wondered if there was a simple way in bash scripting to use the 'test' construct to compare two strings matching a given pattern. In this particular case, I want a pair of numeric strings to match if they have leading zeros in front of either of them. Thanks.
#!/bin/bash
STR1=123
STR2=00123
if [[ "0*${STR1}" == "0*${STR2}" ]]; then
echo "Strings are equal"
else
echo "Strings are NOT equal"
fi
exit 0
Strip any leading zeros from the strings, then check if the results are equal. This solution requires extended pattern support from bash.
shopt -s extglob
if [[ "${STR1##*(0)}" = "${STR2##*(0)}" ]]; then
echo "Strings are equal"
fi
You can also use bash's built-in regular expression support, but it might require two comparisons if you don't know which string has more leading 0s. The test works when the string on the left has at least as many leading 0s as the string on the right.
if [[ $STR1 =~ 0*$STR2 || $STR2 =~ 0*$STR1 ]]; then
echo "Strings are equal"
If you are absolutely sure your strings are numeric, then you should use -eq instead of ==:
if [ $string1 -eq $string2 ]
then
echo "These are equal"
fi
The -eq doesn't care about leading zeros
The problem is that if neither string is numeric (or one string is equal to zero and the other isn't numeric), this will still work:
string1=foo
string2=bar
if [ $string1 -eq $string2 ]
then
echo "These are equal" # This will print, and it shouldn't!
fi
The only way I see getting around this issue is to do something like this:
if expr $string1 + 0 > /dev/null 2&1 && expr $string2 + 0 > /dev/null 2>&1
then # Both strings are numeric!
if [ $string1 -eq $string2 ]
then
echo "Both strings are numeric and equal."
else
echo "Both strings are numeric, but not equal."
elif [ $sring1 = $sring2 ]
then
echo "Strings aren't numeric, but are the same
else
echo "Strings aren't numeric or equal to each other"
fi
The expr will return a non-zero exit code if the string isn't numeric. I can use this in my if to test to see if my strings are in fact numeric or not.
If they are both numeric, I can use my second if with the -eq to test for integer equivalency. Leading zeros are no problem.
The elif is used in case my strings are not numeric. In that case, I test with = which tests string equivalency.
This is how I would make:
#!/bin/bash
STR1=123
STR2=00123
if [ `echo -n ${STR1} | sed 's/^0*//g'` == `echo -n ${STR2} | sed 's/^0*//g'` ]; then
echo "Strings are equal"
else
echo "Strings are NOT equal"
fi
exit 0
The obvious and trivial solution is to explicitly indicate the base of the numbers. Then any leading zeros are insignificant because the numbers will not be interpreted as octal.
if [[ 10#$STR1 -eq 10#$STR2 ]]; then
echo "Numbers are equal"
else
echo "Numbers are NOT equal"
fi
Notice also the switch to -eq for numeric comparison.
This solution uses expr to convert the strings into numeric values. Note an alternative bash method - double parentheses - does NOT work as values with leading zeroes are parsed as octal values. e.g.
STR1=000123
echo $(($STR1)) # outputs 83
SOLUTION
#!/bin/bash
STR1="00123"
STR2="0000123"
n1=$(expr $STR1 + 0)
n2=$(expr $STR2 + 0)
if [ $n1 -eq $n2 ];then
echo "Strings are equal"
else
echo "Strings are NOT equal"
fi
exit 0
Related
I am trying to write a script that filters for certain words.
This is the method I was using. it was working well but when the number of possible matches became too large it doesn't do the job anymore. I am not sure if this method has some sort of limitations. So any alternatives are welcome.
str="He is driving a car"
if [ "$str" != "${str/car/}" ] || [ "$str" != "${str/bus/}" ] || [ "$str" != "${str/truck/}" ] || [ "$str" != "${str/vehicle/}" ];then
echo "Substring found"
else
echo "Substring not found"
fi
I don't think that there's anything wrong with your method, although I would say that it is quite long to write.
Since you're using bash, you can use an extended glob, which reduces the length of your code significantly:
# enable extended globs
shopt -s extglob
# match anything containing car, bus, truck or vehicle
if [[ $str = *#(car|bus|truck|vehicle)* ]]; then
echo "Substring found"
fi
# unset extended glob mode
shopt -u extglob
Or you can simply use the following regular expression matching operator =~ that compares the string on the left to the extended regular expression on the right
str="He is driving a car"
vehicules="car|bus|truck|vehicle"
if [[ $str =~ $vehicules ]]; then
echo "Substring found"
fi
A solution using expr match can also be used.
This worked well for me
case ${str} in car* | bus* | truck* | vehicle*)
echo "Substring found"
esac
How do I check if a variable contains characters (regex) other than 0-9a-z and - in pure bash?
I need a conditional check. If the string contains characters other than the accepted characters above simply exit 1.
One way of doing it is using the grep command, like this:
grep -qv "[^0-9a-z-]" <<< $STRING
Then you ask for the grep returned value with the following:
if [ ! $? -eq 0 ]; then
echo "Wrong string"
exit 1
fi
As #mpapis pointed out, you can simplify the above expression it to:
grep -qv "[^0-9a-z-]" <<< $STRING || exit 1
Also you can use the bash =~ operator, like this:
if [[ ! "$STRING" =~ [^0-9a-z-] ]] ; then
echo "Valid";
else
echo "Not valid";
fi
case has support for matching:
case "$string" in
(+(-[[:alnum:]-])) true ;;
(*) exit 1 ;;
esac
the format is not pure regexp, but it works faster then separate process with grep - which is important if you would have multiple checks.
Using Bash's substitution engine to test if $foo contains $bar
bar='[^0-9a-z-]'
if [ -n "$foo" -a -z "${foo/*$bar*}" ] ; then
echo exit 1
fi
The following program checks if the first parameter is a number, or a not a number. The best I could come up with so far is:
#/bin/bash
if (( $# )) && (( $1 != 0 )) ; then
echo "number"
else
echo "not number"
fi
This works for numbers like 1, -1, 100, etc, for strings a, abc, a431, or if no parameter is given.
It works with a warning for strings like 1a1, 1.3, -1.2
But FAILS (obviously) if the parameter is 0
How could this be fixed? (using arithmetic expressions)
Related: Bash integer comparison
You can press the printf built-in command to do the job:
if printf "%d" ${1:-foo} > /dev/null; then
echo "number"
else
echo "NOT a number"
fi
printf exits with non-zero status if the conversion fails. The ${1-...} notation
provides a known not-number if $1 isn't set.
You could use a regex:
#/bin/bash
if [[ "$1" =~ ^-?[0-9]+$ ]] ; then
echo "number"
else
echo "not number"
fi
If you wanted it to handle floating point, you could tweak it easily enough:
#/bin/bash
if [[ "$1" =~ ^-?[0-9]+$ || "$1" =~ ^-?[0-9]*\.[0-9]+ ]] ; then
echo "number"
else
echo "not number"
fi
I would rather use something like
if [ $1 = "$(echo $1 | awk '{print strtonum($0)}')" ]; then
echo "number"
else
echo "not number"
fi
Solution using extended globs
#/bin/bash
shopt -s extglob
if [[ $1 = *[0-9]* && $1 = ?([+-])*([0-9])?(.*([0-9])) ]]; then
echo "Is a number"
else
echo "Not a number"
fi
Output
$ ./test -1
Is a number
$ ./test 0
Is a number
$ ./test 1
Is a number
$ ./test a
Not a number
$ ./test -1.2
Is a number
$ ./test 0.0
Is a number
$ ./test 1.2
Is a number
$ ./test a.b
Not a number
Might be easier to test for what is not a number:
if (( $# )) ; then
if [[ $1 == *[^0-9]* ]] ; then
echo "Not a number"
else
echo "Is a number"
fi
fi
Note that the above is actually testing only for positive integers. Add +-. to the character class and it will work, sort of, for positive/negative integers and decimal fractions. I say sort of because it would pass 123.456.789 as a number, but it's not valid. You'd need to add additional logic to weed out misplaced or multiple signs and decimal points (and strings like "+", ".", etc.).
I want to check whether a string has at least one
alphabetic character?
a regex could be like:
"^.*[a-zA-Z].*$"
however, I want to judge whether a string has at least one
alphabetic character?
so I want to use, like
if [ it contains at least one alphabetic character];then
...
else
...
fi
so I'm at a loss on how to use the regex
I tried
if [ "$x"=~[a-zA-Z]+ ];then echo "yes"; else echo "no" ;fi
or
if [ "$x"=~"^.*[a-zA-Z].*$" ];then echo "yes"; else echo "no" ;fi
and test with x="1234", both of the above script output result of "yes", so they are wrong
how to achieve my goal?thanks!
Try this:
#!/bin/bash
x="1234"
y="a1234"
if [[ "$x" =~ [A-Za-z] ]]; then
echo "$x has one alphabet"
fi
if [[ "$y" =~ [A-Za-z] ]]; then
echo "Y is $y and has at least one alphabet"
fi
If you want to be portable, I'd call /usr/bin/grep with [A-Za-z].
Use the [:alpha:] character class that respects your locale, with a regular expression
[[ $str =~ [[:alpha:]] ]] && echo has alphabetic char
or a glob-style pattern
[[ $str == *[[:alpha:]]* ]] && echo has alphabetic char
It's quite common in sh scripts to use grep in an if clause. You can find many such examples in /etc/rc.d/.
if echo $theinputstring | grep -q '[a-zA-Z]' ; then
echo yes
else
echo no
fi
I'm relatively new to bash programming and i am currently creating a simple calculator.
It needs to use floating point numbers and check they are so.
I have a checkNumbers function:
function checkNumber {
regExp=[0-9]
if [ $testNo =~ $regExp ]
then
echo "That is a number!"
let check=1
else
echo "Damn! Not A Number!"
fi
}
where i get the user to input a number like this:
while [ $check -eq 0]
do
echo "Please enter a number
read testNo
echo ""
checkNumber
done
This does not work, i get this error:
./calculator.sh: line 39: [: =~: binary operator expected
line 39 is:
if [ $testNo =~ $regExp ]
I have tried lots of Regular Expressions like:
^*[0-9]*$
and
^*[0-9]\.[0.9]*$
etc etc.
also, i have tied other ways of performing the check:
case $testNo
in
^*[0-9]*$) echo "That is a number!";;
*) echo "Error! Not a number!";;
esac
and
if [ $testNo = ^*[0-9]*$ ]
then
echo "etc etc"
else
echo "oops"
fi
I also need this to work with floating point numbers.
could someone please let me know how i go about this?
This regex ^[-+]?[0-9]+\.?[0-9]*$ will match only digits with an optional .:
$ echo 30 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo 30.10 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo 30. | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo +30 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
$ echo -30 | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' && echo Match
Match
I think when you tried ^*[0-9] you wanted ^[0-9]*
Rexeplanation:
^ # Match start of string
[-+]? # Match a leading + or - (optional)
[0-9]+ # Match one or more digit
\.? # Match a literal . (optional, escaped)
[0-9]* # Match zero or more digits
$ # Match the end of the string
Note: this matches numbers followed by a . like 30., not sure if this is acceptable for you.
Edit: Don't quote the regex
testNo=30.00
if [[ $testNo =~ ^[+-]?[0-9]+\.?[0-9]*$ ]]; then
echo Match
fi
>>> Match
To use that type of feature, you need the [[ ... ]] version of the conditional. [ is the "old" test command and doesn't handle regular expressions at all.
#! /bin/bash
function checkNumber {
regExp='^[+-]?([0-9]+\.?|[0-9]*\.[0-9]+)$'
if [[ $testNo =~ $regExp ]]
then
echo "That is a number!"
let check=1
else
echo "Damn! Not A Number!"
fi
}
testNo=1
checkNumber
testNo=-1.2
checkNumber
testNo=+.2
checkNumber
testNo=+0.
checkNumber
testNo=a
checkNumber
testNo=hello2you
checkNumber
$ ./t.sh
That is a number!
That is a number!
That is a number!
That is a number!
Damn! Not A Number!
Damn! Not A Number!
See What is the difference between test, [ and [[ ?.
An explanation on the regex:
^ Anchor at start of string
$ Anchor at end of string
These two make the regex match the whole string passed, partial matches are not allowed.
[+-]
matches either + or -.
[+-]?
makes that part optional, so the above matches exactly +, - or nothing at all.
Then there's an alternation (part1|part2) which will match if part1 or part2 matches.
Part one is:
[0-9]+\.?
which matches one or more (+) digits (but not zero digits/empty set) and an optional .. This handles numbers of the form 123 and 534.. But not just ..
Part two is:
[0-9]*\.[0-9]+
This matches zero or more (*) digits, followed by a ., followed by one or more digits. This matches all other floats like 1.3 or .543 (without exponent notation), but still excludes just ..
#!/bin/bash
#script to validate
while [ true ]; do
clear
echo "Introduce the numeric value: "
read -r var
if [[ $var =~ ^[+-]?([0-9]+)$ ]]
then
echo "You introduced an integer value (without decimals)"
read
else
if [[ $var =~ ^[+-]?([0-9]+\.)$ ]]
then
echo "Incomplete floating value (no values provided at the right of the point)"
read
else
if [[ $var =~ ^[+-]?(\.[0-9]+)$ ]]
then
echo "Incomplete floating value (no values provided at the left of the point)"
read
else
if [[ $var =~ ^[+-]?([0-9]+\.[0-9]+)$ ]]
then
echo "You introduced a correct floating value"
read
else
echo "You introduced something other than a valid numeric value"
read
fi
fi
fi
fi
done
I include an optional dot at the start.
user#debian:~$ testNo=.01
user#debian:~$ if [[ $testNo =~ ^[+-]?[.]?[0-9]+\.?[0-9]*$ ]];
> then
> echo Match;
> fi
Match