我试图使用case命令匹配日期模式,但它不起作用。有人能帮我吗 ?

输入01/05/1900 或者5/6/1900

使用的案例陈述:

ptn="[0-9]|[0-9][0-9]/[0-9]|[0-9][0-9]/[0-9][0-9]|[0-9][0-9][0-9][0-9]"

case "$dt" in 
    $ptn ) echo "valid pattern" ;;
    *) echo "invalid"
esac

但 case 语句中使用的模式并未评估上述 2 个日期输入。

答案1

尝试这个。 case只理解全局变量,因此处理正则表达式并不简单。不过,运营=~商在这里提供了一些解决方案。

#!/bin/bash
INP="01/05/1900"
ptn="^([0-9]|[0-9][0-9])/([0-9]|[0-9][0-9])/([0-9][0-9]|[0-9][0-9][0-9][0-9])$"
if [[ $INP =~ $ptn ]] ; then
    echo valid pattern
else
    echo invalid
fi

如果您想减小正则表达式的大小,^[0-9]{1,2}/[0-9]{1,2}/([0-9]{2}|[0-9]{4})$也同样有效。

答案2

正如@Steve 已经提及case需要一个 glob,而不是正则表达式。你仍然可以使用case,你只需要给它 glob 即可:

#!/bin/sh
dt="$1";
ptn1="[0-9]/[0-9]/[0-9][0-9][0-9][0-9]"           ## N/N/NNNN
ptn2="[0-9][0-9]/[0-9]/[0-9][0-9][0-9][0-9]"      ## NN/N/NNNN
ptn3="[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]"      ## N/NN/NNNN
ptn4="[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]" ## NN/NN/NNNN

case "$dt" in 
    $ptn1|$pt2|$pt3|$pt4 ) echo "$dt : valid pattern" ;;
    *) echo "$dt : invalid" ;;
esac

然后,像这样运行脚本:

foo.sh 15/6/1900

例如:

$ for i in 05/6/1900 5/06/1900 05/06/1900 05/06/19 123/123/123; do foo.sh $i; done
05/6/1900 : valid pattern
5/06/1900 : valid pattern
05/06/1900 : valid pattern
05/06/19 : invalid
123/123/123 : invalid

如果您可以使用 GNU 工具(特别是 GNU date),您还可以使用如下技巧:

#!/bin/sh
dt="$1";
date -d $dt &> /dev/null &&
        echo "$dt : Valid pattern" ||
                echo "$dt :Invalid pattern"

然后,在与之前相同的测试输入上:

$ for i in 05/6/1900 5/06/1900 05/06/1900 05/06/19 123/123/123; do foo.sh $i; done
05/6/1900 : Valid pattern
5/06/1900 : Valid pattern
05/06/1900 : Valid pattern
05/06/19 : Valid pattern
123/123/123 :Invalid pattern

正如您所看到的,通过这种方式,即使在年份指定为而不是 的情况下,您也可以获得更有效的YY模式YYYY

答案3

您呈现的“模式”:

ptn="[0-9]|[0-9][0-9]/[0-9]|[0-9][0-9]/[0-9][0-9]|[0-9][0-9][0-9][0-9]"

基本图案

不适用于案例所使用的基本模式。基本模式不允许|。也不允许使用计数{a,b}(在 man 7 正则表达式中称为“bound”)。要使其在语句中工作,case您需要显式构建每个模式:

ptn1="[0-9]/[0-9]/[0-9][0-9]"                       # n  / n  / nn
ptn2="[0-9]/[0-9]/[0-9][0-9][0-9][0-9]"             # n  / n  / nnnn
ptn3="[0-9]/[0-9][0-9]/[0-9][0-9]"                  # n  / nn / nn
ptn4="[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]"        # n  / nn / nnnn
ptn5="[0-9][0-9]/[0-9]/[0-9][0-9]"                  # nn / n  / nn
ptn6="[0-9][0-9]/[0-9]/[0-9][0-9][0-9][0-9]"        # nn / n  / nnnn
ptn7="[0-9][0-9]/[0-9][0-9]/[0-9][0-9]"             # nn / nn / nn
ptn8="[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]"   # nn / nn / nnnn

然后像这样使用它:

case $dt in 
    $ptn1|$ptn2|$ptn3|$ptn4|$ptn5|$ptn6|$ptn7|$ptn8 )
        echo "$dt : valid pattern" ;;
    *)  
        echo "$dt : invalid" ;;
esac

(外壳)扩展模式。

某些 shell 允许使用扩展模式。
在 ksh 中称为模式列表,在 bash 中称为扩展模式 (extglob)。

#!/usr/bin/ksh
ptn="{2}({1,2}([0-9])/){2,4}([0-9])" 
while read dt; do
    printf 'line tested %20s ' "$dt"
    [[ $dt == $ptn ]] && echo "valid pattern" ||  echo "invalid"
done <"infile"

正则表达式

但是您提供的模式可以简化为这个更简单的(BRE)正则表达式:

([0-9]{1,2}/){2}[0-9]{2,4}

这是:

  • 一位或两位数字后跟/:[0-9]{1,2}/
  • 重复上述两次:( ){2}
  • 附加两位到四位数字:[0-9]{2,4}

#!/bin/bash
# also works in ksh and zsh.
reg="^([0-9]{1,2}/){2}[0-9]{2,4}$"

while read dt; do
    printf 'line tested %20s ' "$dt"
    [[ $dt =~ $reg ]] && echo "valid pattern" ||  echo "invalid"
done <"infile"

BRE 正则表达式。

如果您无法使用 ksh、bash 或 zsh,请对正则表达式加反引号:

reg="^\([0-9]\{1,2\}/\)\{2\}[0-9]\{2,4\}$"

并使用expr(例如也可以使用sed和awk):

reg="^\([0-9]\{1,2\}/\)\{2\}[0-9]\{2,4\}$"

while read dt; do
    printf 'line tested %20s ' "$dt"
    expr "$dt" : "\($reg\)" >/dev/null && echo "valid pattern" ||  echo "invalid"
done <"infile"

答案4

我喜欢特登的回答。

关于正则表达式这个问题的一部分,你的模式无效。你应该使用\所有特殊字符进行转义,如下所示:

([0-9]|[0-9][0-9])\/([0-9]|[0-9][0-9])\/([0-9][0-9]|[0-9][0-9][0-9][0-9])

对于日期等已知数据集,不要尝试重新发明轮子,因为互联网上实际上有数千个用于匹配日期的答案。如果您想创建更个性化的内容,请使用正则表达式助手,例如我个人最喜欢的正则表达式.com根据您的意愿测试和增强您的模式。

编辑:使用转义字符,您的代码将捕获一些日期,但它仍然不完美,因为它永远不会捕获01/01/2016,而是只会捕获01/01/20。年份的“优先级”是 2 个数字,而不是 4 个。您需要切换它们以找到第一个最大的匹配项,如果失败,它将捕获最小的匹配项:

([0-9]|[0-9][0-9])\/([0-9]|[0-9][0-9])\/([0-9][0-9][0-9][0-9]|[0-9][0-9])

相关内容