在 case 语句中使用正则表达式匹配数字

在 case 语句中使用正则表达式匹配数字

我想检查 shell 脚本的参数是否为整数(即​​非负整数:0、1、2、3、…、17、…、42、…等,但不是 3.1416 或 −5 )以十进制表示(所以不像 0x11 或 0x2A)。如何使用正则表达式作为条件(以匹配数字)编写 case 语句?我尝试了几种不同的方法(例如,[0-9]+^[0-9][0-9]*$);它们都不起作用。如下例所示,有效数字通过旨在捕获它们的数字正则表达式并与通配符匹配 *

i=1
let arg_n=$#+1

while (( $i < $arg_n )); do
    case ${!i} in
    [0-9]+)
        n=${!i}
        ;;
    *)
        echo 'Invalid argument!'
        ;;
    esac
    let i=$i+1
done

输出:

$ ./cmd.sh 64
Invalid argument!

答案1

case不使用正则表达式,它使用图案

对于“1 位或更多数字”,请执行以下操作:

shopt -s extglob
...
    case ${!i} in
        +([[:digit:]]) )
            n=${!i}
            ;;
    ...

如果要使用正则表达式,请使用=~其中的运算符[[...]]

if [[ ${!i} =~ ^[[:digit:]]+$ ]]; then
    n=${!i}
else
    echo "Invalid"
fi

答案2

正如格伦所说, “case不使用正则表达式,它使用图案”。作为重击(1)说,

case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac

        Acase命令首先展开word,并尝试将其与每个匹配pattern反过来,使用与路径名扩展相同的匹配规则(请参阅路径名扩展以下)。

相似地,POSIX 规范说,

… 每个图案…应与扩展进行比较单词,根据中描述的规则模式匹配表示法 ……

所以图案是路径名扩展模式,又名通配符,又名通配符,如ls -l -- *.sh或中的rm -- *.bak

当然,shopt -s extglob[[ … =~ … ]] 是自切片面包以来最简洁的东西,但它们不是 POSIX,了解如何使用原始工具可能会很有用。例如,多年来,程序员通过检查字符串是否为数字来检查字符串是否为数字。不是一个号码。您已将数字定义为(全部)由一位或多位数字组成的字符串。因此,如果字符串为空,或者包含非数字字符,则该字符串不是数字。我们可以使用case如下语句来测试这些条件:

case "$1" in
    ("")
        # null
        ;;
    (*[!0-9]*)
        # contains non-numeric character(s)
        ;;
    (*)
        # is a whole number (non-negative integer)
esac

其中[!0-9]是旧式 shell 的表达方式[^0-9],当然,它表示除数字之外的任何字符。 ([!…]两者[^…]都在 bash 中工作。POSIX  [!…]需要工作;其结果[^…]未指定。)如果您不关心字符串是哪种非数字,则可以组合非数字模式:

case "$1" in
    ("" | *[!0-9]*)
        # not a number
        ;;
    (*)
        # is a number
esac

作为练习,这里有一个case处理任何类型实数的语句 - 准确地说,是一个由一个或多个数字组成的字符串,可选地在某处带有句点 ( ),并且可选地在开头带有.减号 ( )。-

case "$1" in
    (*[!-.0-9]*)
        # contains non-numeric character(s)
        ;;
    (*?-*)
        # contains '-' somewhere other than the first position
        ;;
    (*.*.*)
        # contains multiple decimal points
        ;;
    (*)
        case "$1" in
            (*[0-9]*)
                # is a real number
                ;;
            (*)
                # not a number
        esac
esac

我添加了case-within-a-case来验证该字符串确实至少包含一位数字。在整数示例中这不是必需的,因为我测试了字符串是否为空;我已从本声明中删除了一项测试。如果没有第二个case,单个-或单个.- 甚至-.- 将有资格作为数字。当然,我们可以添加模式来处理这些异常,但这可能会变得复杂。 (例如,我几乎发布了这个答案,但没有意识到这-.是例外之一。)我相信上述方法更加灵活和稳健。

当然,非数字模式也可以在这里组合: (*[!-.0-9]* | *?-* | *.*.*)

答案3

case在语句中使用正则表达式匹配数字,您需要一个通配符支持正则表达式的 shell。我只知道 ksh93 与这些。

使用 ksh93 glob,您可以在 glob 模式中执行~(E)^[0-9]+$~(E:^[0-9]+$)使用扩展正则表达式,或者使用类似 perl 的正则表达式(也适用于基本正则表达式、增强正则表达式、SysV 正则表达式)。E~(P)^\d+$GXV

所以:

#! /bin/ksh93 -
for i do
  case $i in
    (~(E)^[0-9]+$)
      n=$i;;
    (*)
      echo >&2 'Invalid argument!'
      usage
  esac
done

相关内容