使用Bash编写的Shell脚本,在while循环中使用正则表达式。

3

您好,我正在尝试验证用户输入,确保其不为空并且是数字或带有小数点。

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

我只使用了第一个条件就能够顺利运行。当我加入第二个条件时,我的程序无法运行。

----编辑----------

好的,我尝试进行更改,程序可以运行。但是当我输入数字时,它仍然提示我输入。

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

你对第二个条件具体想要做什么? - SMA
正则表达式部分的右侧不应被引用。 - Marc Bredt
我认为你的意思是不等于。 - Avinash Raj
@almasshaikh 我正在尝试使用正则表达式来检查用户输入是否为数字。 - Tommy Yap
3个回答

3

正则表达式可以与运算符=~一起使用,而不是您使用的~=

另外一个二进制运算符=~也可用,其优先级与==和!=相同。当使用它时,右侧的字符串被视为扩展的正则表达式并相应地进行匹配(如在regex(3)中)。如果字符串与模式匹配,则返回值为0,否则为1。如果正则表达式在语法上不正确,则条件表达式的返回值为2。如果启用了shell选项nocasematch,则匹配将忽略字母字符的大小写。由于普通引号字符在括号中失去了其含义,因此必须谨慎处理正则表达式中的括号表达式。如果模式存储在shell变量中,则引用变量扩展会强制整个模式作为字符串进行匹配。在正则表达式中由括号子表达式匹配的子字符串保存在数组变量BASH_REMATCH中。具有索引0的BASH_REMATCH元素是与整个正则表达式匹配的部分。具有索引n的BASH_REMATCH元素是与第n个括号子表达式匹配的字符串部分。

考虑以下示例(0表示真/匹配,1表示假/未匹配)

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

使用这个方法来检查一个数字

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

更新:如果您只是想检查用户输入的数字,请将上面学到的课程与您的需求相结合。我认为您的条件不适合。也许这段代码片段可以完全解决您的问题。

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

这段代码仅在${num}不是数字时请求输入。在第一次运行时,${num}未设置,因此它将不符合至少一个数字,然后${num}求值为空字符串。之后它只包含输入的内容。

谢谢您的解释。但是我似乎无法做到这一点。当我输入“abc”时,它们仍然被接受,并且没有提示再次输入。re = ^ [0-9] + $; while [“$ book_price”==“”] && [[“$ book_price”=~$ {re}]] - Tommy Yap

1
你的错误很简单; 变量不能同时为空和为数字。也许你是想使用“或”符号||,而不是“与”符号&&
你也可以使用通配符模式来完成这个任务。
while true; do
  read -r -p "Enter a price: " num
  case $num in
    "" | *[!.0-9]* | *.*.*) echo invalid ;;
    *) break;;
esac

1

首先,OP问题中展示了一个经典的逻辑陷阱:

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

这里的问题在于&&,它基本上意味着左侧表达式一旦为false,整个表达式就为false。也就是说,一旦有人输入非空响应,就会中断循环,正则表达式测试就不会被使用。为了解决这个逻辑问题,应该考虑将&&更改为||,即:
while [ "$num" == "" ] || [ "$num" != $re ]

第二个问题是我们正在测试正则表达式模式的负匹配。因此,这需要分两步完成。首先,我们需要使用[[ "$num" =~ $re ]]进行正则表达式测试。然后,我们需要寻找负匹配,即追加一个!,如下所示:
while [ "$num" == "" ] || ! [[ "$num" =~ $re ]

许多人发现,在这一步骤中,实际上没有必要测试空字符串。这个边界条件已经被正则表达式本身所覆盖,因此我们优化掉了冗余的测试。现在答案简化为:

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

除了上述观察,以下是我关于正则表达式的笔记(其中一些观察已经整理自其他答案):
  • 可以使用[[ "$str" =~ regex ]]语法测试正则表达式
  • 正则表达式匹配$? == 0(0 == 无错误)
  • 正则表达式不匹配$? == 1(1 == 错误)
  • 引用正则表达式时似乎不起作用。建议使用[0-9]而不是"[0-9]"

要实现数字验证,以下模式似乎有效:

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

您可以将正则表达式过滤器与常规算术过滤器混合使用,以获得非常好的验证结果:

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

注意:我使用了${str?}语法(而不是$str)进行变量扩展,因为它展示了捕获拼写错误的良好实践。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接