在Bash脚本中检查字符串是否与正则表达式匹配

367

我的脚本接收的一个参数是以以下格式给出的日期:yyyymmdd

我想要检查我是否获得了有效的输入日期。

我该如何做呢? 我尝试使用正则表达式,如下所示:[0-9]\{\8}


检查格式是否正确很容易。但我不认为你可以在Bash(使用内置功能)中检查日期是否有效。 - RedX
5个回答

571

您可以使用测试结构[[ ]]以及正则表达式匹配操作符=~来检查字符串是否与正则表达式模式匹配(文档)。

对于您的特定情况,您可以编写:

[[ "$date" =~ ^[0-9]{8}$ ]] && echo "yes"

或者更准确的测试:

[[ "$date" =~ ^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$ ]] && echo "yes"
#             |\______/\______*______/\______*__________*______/|
#             |   |           |                  |              |
#             |   |           |                  |              |
#             | --year--   --month--           --day--          |
#             |          either 01...09      either 01..09      |
#      start of line         or 10,11,12         or 10..29      |
#                                                or 30, 31      |
#                                                          end of line

也就是说,你可以在 Bash 中定义一个匹配你需要的格式的正则表达式。这样你就可以执行以下操作:

That is, you can define a regex in Bash matching the format you want. This way you can do:

[[ "$date" =~ ^regex$ ]] && echo "matched" || echo "did not match"

如果测试成功,则执行&&后的命令,如果测试失败,则执行||后的命令。

请注意,这是基于Aleks-Daniel Jakimenko在在bash中验证用户输入日期格式的解决方案中的解决方案。


在其他shell中,您可以使用grep。如果您的shell符合POSIX标准,请执行以下操作

(echo "$date" | grep -Eq  ^regex$) && echo "matched" || echo "did not match"

在不符合POSIX标准的fish中,你可以这样做:

echo "$date" | grep -Eq "^regex\$"; and echo "matched"; or echo "did not match"

注意: 这些便携式 grep 解决方案不是防水的!例如,它们可能会被包含换行符的输入参数欺骗。首次提到的特定于 bash 的正则表达式检查没有此问题。


4
如果你使用shfish或其他功能较少的shell,使用grep似乎是最好的选择。 - tomekwi
1
@tomekwi,当然可以。但是这个问题被标记为[tag:bash],因此最好给出一个在bash中有意义的答案。您始终可以列出替代的sh兼容解决方案,但绝不能成为主要答案。 - Aleks-Daniel Jakimenko-A.
1
@fedorqui 此正则表达式允许月份和/或日期为零。例如,这些无效的日期被代码视为有效:20160015、20161200。我认为以下模式更准确:^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$。 - thanos.a
1
注意:使用 grep 的解决方案接受多行值,其中每行包含一个日期:例如 printf '%s\n%s\n' "$date" "$date" | grep … 将被接受,这可能不是预期的。 - joki
1
哇!我从来没有想过我可以像这样使用批处理运算符(&&,我称之为原子批处理,因为它将左右值转换为一个原子批处理)。虽然它比if语句不太易读,但在我看来,它非常节省空间,通常值得权衡。谢谢。 - Nate T
显示剩余15条评论

86

在Bash 3版本中,您可以使用“=~”运算符:

if [[ "$date" =~ ^[0-9]{8}$ ]]; then
    echo "Valid date"
else
    echo "Invalid date"
fi

参考文献:http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF

注意:自Bash 3.2版本起,在双方括号[[ ]]中的匹配运算符中不再需要引用。


46

测试一个字符串是否为正确的日期的好方法是使用命令 date:

if date -d "${DATE}" >/dev/null 2>&1
then
  # do what you need to do with your date
else
  echo "${DATE} incorrect date" >&2
  exit 1
fi

来自评论:可以使用格式化

if [ "2017-01-14" == $(date -d "2017-01-14" '+%Y-%m-%d') ] 

13
非常好的答案,因为它让日期函数处理日期,而不是容易出错的正则表达式。 - Ali
1
这对于检查广泛的日期选项很有用,但如果您需要验证特定的日期格式,它能做到吗?例如,如果我执行 date -d 2017-11-14e,它会返回 Tue Nov 14 05:00:00 UTC 2017,但那会破坏我的脚本。 - Josiah
1
你可以像这样使用:if [ "2017-01-14" == $(date -d "2017-01-14" '+%Y-%m-%d') ]它会测试日期是否正确,并检查结果是否与您输入的数据相同。顺便说一下,务必小心本地化日期格式(例如月-日-年与日-月-年)。 - Django Janny
1
可能无法正常工作,这取决于您的语言环境。使用 MM-DD-YYYY 格式的美国日期在世界其他地方都不适用,应该使用 DD-MM-YYYY(欧洲)或 YYYY-MM-DD(亚洲某些地区)。 - Paul
@Paul,哪些可能不起作用?正如评论中所写,可以使用格式选项... - Betlista
显示剩余2条评论

6
=~ Bash 运算符除其他答案之外,还支持扩展正则表达式(ERE)——扩展正则表达式
作为示例,一个支持多个测试的函数可在多个参数中提供:
#!/bin/bash

#-----------#
# Functions #
#-----------#

function RT
{
    declare __line;

    for __line in "${@:2}";
    do
        if ! [[ "$__line" =~ $1 ]];
        then
            return 1;
        fi
    done

    return 0;
}

#-----------#
# Main      #
#-----------#

regex_v='^[0-9]*$';
value_1_v='12345';
value_2_v='67890';

if RT "$regex_v" "$value_1_v" "$value_2_v";
then
    printf 'Valid';
else
    printf 'Invalid';
fi

描述

函数 RT正则表达式测试

# Declare a local variable for a loop.

declare __line;

# Loop for every argument's value except the first - regex rule

for __line in "${@:2}";

# Test the value and return a **non-zero** return code if failed.
# Alternative: if [[ ! "$__line" =~ $1 ]];

if ! [[ "$__line" =~ $1 ]];

# Return a **zero** return code - success.

return 0;

主要代码

# Define arguments for the function to test

regex_v='^[0-9]*$'; # Regex rule
value_1_v='12345'; # First value
value_2_v='67890'; # Second value

# A statement which runs the function with specified arguments
# and executes `printf 'Valid';` if succeeded, else - `printf 'Invalid';`

if RT "$regex_v" "$value_v";

例如,可以通过向循环附加计数器并将其值打印到stderr来指出失败的参数。

相关

=~操作符右侧的引号使其变成字符串,而不是RegularExpression

来源


1
当需要确定日期的字符序列是否正确时,正则表达式的使用可能会有所帮助,但它不能轻松地确定日期是否有效。以下示例将通过正则表达式,但都是无效日期:20180231、20190229、20190431。
因此,如果您想验证日期字符串(我们称之为“datestr”)是否具有正确的格式,则最好使用date解析它,并要求date将字符串转换为正确的格式。如果两个字符串相同,则您具有有效的格式和有效的日期。
if [[ "$datestr" == $(date -d "$datestr" "+%Y%m%d" 2>/dev/null) ]]; then
     echo "Valid date"
else
     echo "Invalid date"
fi

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