check if parameter is integer with numeric expression - regex

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.).

Related

Check if a string contains valid pattern in Bash

I have a file a.txt contains a string like:
Axxx-Bxxxx
Rules for checking if it is valid or not include:
length is 10 characters.
x here is digits only.
Then, I try to check with:
#!/bin/bash
exp_len=10;
file=a.txt;
msg="checking string";
tmp="File not exist";
echo $msg;
if[ -f $file];then
tmp=$(cat $file);
if[[${#tmp} != $exp_len ]];then
msg="invalid length";
elif [[ $tmp =~ ^[A[0-9]{3}-B[0-9]{4}]$]];then
msg="valid";
else
msg="invalid";
fi
else
msg="file not exist";
fi
echo $msg;
But in valid case it doesn't work...
Is there someone help to correct me?
Thanks :)
Other than the regex fix, your code can be refactored as well, moreover there are syntax issues as well. Consider this code:
file="a.txt"
msg="checking string"
tmp="File not exist"
echo "$msg"
if [[ -f $file ]]; then
s="$(<$file)"
if [[ $s =~ ^A[0-9]{3}-B[0-9]{4}$ ]]; then
msg="valid"
else
msg="invalid"
fi
else
msg="file not exist"
fi
echo "$msg"
Changes are:
Remove unnecessary cat
Use [[ ... ]] when using bash
Spaces inside [[ ... ]] are required (your code was missing them)
There is no need to check length of 10 as regex will make sure that part as well
As mentioned in comments earlier correct regex should be ^A[0-9]{3}-B[0-9]{4}$ or ^A[[:digit:]]{3}-B[[:digit:]]{4}$
Note that a regex like ^[A[0-9]{3}-B[0-9]{4}]$ matches
^ - start of string
[A[0-9]{3} - three occurrences of A, [ or a digit
-B - a -B string
[0-9]{4} - four digits
] - a ] char
$ - end of string.
So, it matches strings like [A[-B1234], [[[-B1939], etc.
Your regex checking line must look like
if [[ $tmp =~ ^A[0-9]{3}-B[0-9]{4}$ ]];then
See the online demo:
#!/bin/bash
tmp="A123-B1234";
if [[ $tmp =~ ^A[0-9]{3}-B[0-9]{4}$ ]];then
msg="valid";
else
msg="invalid";
fi
echo $msg;
Output:
valid
Using just grep might be easier:
$ echo A123-B1234 > valid.txt
$ echo 123 > invalid.txt
$ grep -Pq 'A\d{3}-B\d{4}' valid.txt && echo valid || echo invalid
valid
$ grep -Pq 'A\d{3}-B\d{4}' invalid.txt && echo valid || echo invalid
invalid
With your shown samples and attempts, please try following code also.
#!/bin/bash
exp_len=10;
file=a.txt;
msg="checking string";
tmp="File not exist";
if [[ -f "$file" ]]
then
echo "File named $file is existing.."
awk '/^A[0-9]{3}-B[0-9]{4}$/{print "valid";next} {print "invalid"}' "$file"
else
echo "Please do check File named $file is not existing, exiting from script now..."
exit 1;
fi
OR In case you want to check if line in your Input_file should be 10 characters long(by seeing OP's attempted code's exp_len shell variable) then try following code, where an additional condition is also added in awk code.
#!/bin/bash
exp_len=10;
file=a.txt;
msg="checking string";
tmp="File not exist";
if [[ -f "$file" ]]
then
echo "File named $file is existing.."
awk -v len="$exp_len" 'length($0) == len && /^A[0-9]{3}-B[0-9]{4}$/{print "valid";next} {print "invalid"}' "$file"
else
echo "Please do check File named $file is not existing, exiting from script now..."
exit 1;
fi
NOTE: I am using here -f flag to test if file is existing or not, you can change it to -s eg: -s "$file" in case you want to check file is present and is of NOT NULL size.

How to check if string contains characters in regex pattern in shell?

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

Bash string comparison w/ leading zeros

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

Bash RegEx to check floating point numbers from user input

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

shell scripting and regular expression

#!bin/bash
echo enter your password :
read password
passlength=$(echo ${#password})
if [ $passlength -le 8 ];
then
echo you entered correct password
else
echo entered password is incorrect
fi
if [[$password == [a-z]*[0-9][a-z]*]];
then
echo match found
else
echo match not found
fi
I am not getting what's wrong with this code. If I enter any string as a password, let's say hello123, it gives me an error:
hello123 : command not found
What is wrong with my script?
You can do the following to make it work cross-platforms with any the bourne shell (/bin/sh) based shell, no bash specific primitives -
echo "$password" | grep -q "[a-z]*[0-9][a-z]*"
if [ $? -eq 0 ] ;then
echo "match found"
else
echo "match not found"
fi
Also feel free to use quotes around the variable names. It will save you hours and hours worth of useless debugging. :)
Technically it should give you an error like [[hello123 : command not found.
The issue is that [[$password is not expanded how you think it is. Bash will first resolve the $password variable to what you entered (i.e. hello123). This will yield the string [[hello123 which bash will then try to invoke (and fail, as there is nothing with that name).
Simply add a space () after [[ and bash will recognise [[ as the command to run (although it is a builtin).
if [[ "$password" == [a-z]*[0-9][a-z]* ]]
then
...
The corrected script is below. The errors were:
#!/bin/bash, not #!bin/bash
To read password length, just do passlength=${#password}, not
passlength=$(echo ${#password})
Always put a space after [ or [[
#!/bin/bash
echo "enter your password :"
read password
passlength=${#password}
if [[ $passlength -le 8 ]]
then
echo "you entered correct password"
else
echo "entered password is incorrect"
fi
if [[ $password == [a-z]*[0-9][a-z]* ]]
then
echo "match found"
else
echo "match not found"
fi
In the bash [[ construct, the == operator will match glob-style patterns, and =~ will match regular expressions. See the documentation.
#!/bin/bash
read -s -p "Enter Password: " password
password_length=${#password}
if [ $password_length -lt 8 -o $password_length -gt 20 ] ;then
echo -e "Invalid password - should be between 8 and 20 characters in length.";
echo ;
else
# Check for invalid characters
case $password in
*[^a-zA-Z0-9]* )
echo -e "Password contains invalid characters.";
echo ;
;;
* )
echo "Password accepted.";
echo ;
break;
;;
esac
fi
More tuned example..
Try to replace line
if [[$password == [a-z]*[0-9][a-z]*]];
with following
if echo "$password" | grep -qs '[a-z]*[0-9][a-z]*'
HTH