简体   繁体   中英

shell script in bash using regex in while loop

Hi i am try to validate user inputs to be not empty and is a number or with decimal

re='^[0-9]+$'
while [ "$num" == "" ] && [[ "$num" ~= $re ]]
do 
echo "Please enter the price : "
read num
done

I was able to run smooth with just the 1st condition. When i add 2nd condition my program couldn't run.

----EDIT----------

Ok i try changing and the program run. But when i enter a number it still prompting for input.

re='^[0-9]+$'
while [ "$num" == "" ] && [ "$num" != $re ]
do 
echo "Please enter the price : "
read num
done

regualar expression can be used with the operator =~ not ~= like you used it.

An additional binary operator, =~, is available, with the same prece dence as == and !=. When it is used, the string to the right of
the operator is considered an extended regular expression and matched accordingly (as in regex(3)). The return value is 0 if the string matches the pattern, and 1 otherwise. If the regular expression is syntactically incorrect, the conditional expression's return value is 2. If the shell option nocasematch is enabled, the match is performed without regard to the case of alphabetic characters. Any part of the pattern may be quoted to force the quoted portion to be matched as a string. Bracket expressions in regular expressions must be treated carefully, since normal quoting characters lose their meanings between brackets. If the pattern is stored in a shell variable, quoting the variable expansion forces the entire pattern to be matched as a string. Substrings matched by parenthesized subexpressions within the regular expression are saved in the array variable BASH_REMATCH. The element of BASH_REMATCH with index 0 is the portion of the string matching the entire regular expression. The element of BASH_REMATCH with index n is the portion of the string matching the nth parenthesized subexpression.

consider theese examples (0 true/match, 1 false/no match)

re=^[0-9]+; [[ "1" =~ ${re} ]]; echo $? # 0
re=^[0-9]+; [[ "a" =~ ${re} ]]; echo $? # 1
re=^[0-9]+; [[ "a1" =~ ${re} ]]; echo $? # 1
re=^[0-9]+; [[ "1a" =~ ${re} ]]; echo $? # 0 because it starts with a number

use this one to check for a number

re=^[0-9]+$; [[ "1a" =~ ${re} ]]; echo $? # 1 because checked up to the end
re=^[0-9]+$; [[ "11" =~ ${re} ]]; echo $? # 0 because all nums

UPDATE: If you just want to check if the user inputs a number combine the lesson learned above with your needs. i think your conditions do not fit. perhaps this snippet solves your issue completely.

#!/bin/bash
re=^[0-9]+$
while ! [[ "${num}" =~ ${re} ]]; do
    echo "enter num:"
    read num
done

This snippet just requests input if ${num} is NOT ( ! ) a number. During the first run ${num} is not set so it will not fit at least one number, ${num} then evaluates to an empty string. Afterwards it just contains the input entered.

Your error is simple; the variable can't be both empty and a number at the same time. Maybe you mean || "or" instead of && "and".

You can do this with glob patterns as well.

while true; do
  read -r -p "Enter a price: " num
  case $num in
    "" | *[!.0-9]* | *.*.*) echo invalid ;;
    *) break;;
esac

First off, there is the classic logic trap demonstrated in the OP's question:

while [ "$num" == "" ] && [ "$num" != $re ]

The issue here is the && which pretty much means the moment the left expression is false , the entire expression is false . ie the moment somebody types a non empty response, it breaks the loop and the regular expression test is never used. To fix the logic problem, one should consider changing && to || , ie

while [ "$num" == "" ] || [ "$num" != $re ]

The second issue, is we are testing for negative matches to regular expression, pattern. So, this is done in two parts, one we need to use [[ "$num" =~ $re ]] for regular expression testing. Then, we need to look for negative matches, ie append a ! which yields:

while [ "$num" == "" ] || ! [[ "$num" =~ $re ]

Having got this far, many people observed that there is actually no need to test for the empty string. That edge condition is already covered by the regular expression itself, so, we optimize out the redundant test. The answer now reduces to:

while ! [[ "$num" =~ $re ]

In addition to the above observation, here are my notes about regular expression ( some of the observation has been collated from other answers ):

  • regular expressions can be tested with the [[ "$str" =~ regex ]] syntax
  • regular expressions match with $? == 0 $? == 0 ( 0 == no error )
  • regular expressions do not match with $? == 1$? == 1 ( 1 == error )
  • regular expressions do not seem to work when quoted. recommend using [0-9] not "[0-9]"

To implement a number validation, the following pattern seems to work:

str=""
while ! [[ "${str?}" =~ ^[0-9]+$ ]]
do
  read -p "enter a number: " str
done

You can mix regular expression filters with regular arithmetic filters for some really nice validation results:

str=""
while ! [[ "${str?}" =~ ^[0-9]+$ ]] \
    || (( str < 1 || str > 15 ))
do
  read -p "enter a number between 1 and 15: " str
done

NB I used the ${str?} syntax ( instead of $str ) for variable expansion as it demonstrates good practice for catching typos.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM