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


204

我的脚本收到的参数之一是以下格式的日期:yyyymmdd

我想检查输入的日期是否有效。

我怎样才能做到这一点?我正在尝试使用如下正则表达式:[0-9]\{\8}


检查格式是否正确很容易。但是我不认为您可以使用bash(内置)检查日期是否有效。
RedX 2014年

Answers:


316

您可以使用测试构造[[ ]]以及正则表达式匹配运算符=~来检查字符串是否与正则表达式模式匹配。

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

[[ $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     end of line
# start of line            or 10,11,12         or 10..29
#                                              or 30, 31

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

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

其中命令之后&&被执行,如果测试是成功的,和命令之后||,如果测试不成功被执行。

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


在其他Shell中,可以使用grep。如果您的外壳符合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"

19
我知道这一点,但我也想考虑谁在问以及他们对bash有多远。如果我们提供非常复杂的条件,他们将不会学到任何东西,只要有其他疑问,他们就会回来。我希望给出一个更容易理解的答案。
fedorqui'SO停止伤害

7
h 好吧,学习的唯一方法就是阅读很多好的代码。如果您提供了易于理解但不建议使用的错误代码,那是一种不好的教学方法。我也很确定对于那些刚开始学习bash的人(可能已经知道另一种语言的某些知识)比grep带有-Eflag的命令更容易理解regex的bash语法。
Aleks-Daniel Jakimenko-A。

8
@ Aleks-DanielJakimenko我再次浏览了这篇文章,现在我同意最好使用bash regex。感谢您指出正确的方向,并更新了答案。
fedorqui'SO停止伤害

4
给予好评,允许使用它有点出乎比OP的问题,在sh,例如..
Dereckson

3
如果使用或其他装备较少的外壳sh,则使用grep的@ Aleks-DanielJakimenko似乎是最佳选择fish
tomekwi

47

在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开始,不再需要在双括号[[]]中使用匹配运算符的引号


20
您不应该在常规表达式中使用char“吗?因为当我使用表达式时不起作用
Dawid Drozd 2015年

此外,{和}的反斜杠转义同样存在问题。
kbulgrien

32

测试字符串是否正确的日期的一种好方法是使用命令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') ] 

9
高度评价您的答案,因为它可以让日期函数处理日期而不是易于出错的正则表达式'
阿里(Ali)

这对于检查较宽的日期选项很有用,但是如果您需要验证特定的日期格式,可以这样做吗?例如,如果我这样做,date -d 2017-11-14e它将返回UTC 2017 Tue Nov 14 05:00:00 UTC,但这会破坏我的脚本。
乔西亚

1
您可以使用类似的方法:if [“ 2017-01-14” == $(date -d“ 2017-01-14”'+%Y-%m-%d')]它测试日期是否正确并检查结果是否与您输入的数据相同。顺便说一句,要特别注意本地化的日期格式(例如,月-日-年与日-月-年)
Django Janny

1
可能不起作用,具体取决于您的区域设置。使用DD-MM-YYYY(欧洲)或YYYY-MM-DD(亚洲的某些地方)的美国格式日期使用MM-DD-YYYY在世界上其他任何地方都无法使用
保罗

@Paul,什么可能行不通?正如评论中所写,可以使用格式选项...
Betlista

4

我会使用expr match而不是=~

expr match "$date" "[0-9]\{8\}" >/dev/null && echo yes

这比当前接受的使用方法更好,=~因为它=~还会匹配空字符串,恕我直言,它不应该。假设badvar未定义,则[[ "1234" =~ "$badvar" ]]; echo $?给出(不正确)0,而expr match "1234" "$badvar" >/dev/null ; echo $?给出正确的结果1

我们必须使用>/dev/nullhide expr match输出值,它是匹配的字符数,如果找不到匹配的则为0。请注意,其输出值退出状态不同。如果找到匹配项,则退出状态为0,否则为1。

通常,的语法为expr

expr match "$string" "$lead"

要么:

expr "$string" : "$lead"

$lead正则表达式在哪里。它的exit status将是真正的(0),如果lead匹配的领先的切片string(是否有此名字吗?)。例如,expr match "abcdefghi" "abc"退出true,但expr match "abcdefghi" "bcd"退出false。(@Carlo Wood指出了这一点。


7
=~不匹配空字符串,则在给出的示例中将字符串与空模式匹配。语法为string =~ pattern,并且空模式匹配所有内容。
bstpierre

2
这不匹配子字符串,它返回(到stdout)匹配的前导字符数,并且如果至少匹配了1个字符,则退出状态为true。这就是为什么空字符串(匹配0个字符)的退出状态为false的原因。例如expr match "abcdefghi" "^" && echo Matched || echo No match-和expr match "abcdefghi" "bcd" && echo Matched || echo No match-都返回"0\nNo match"。匹配"a.*f"将返回的位置"6\nMatched"。因此,在您的示例中也不需要使用'^',并且已经暗示了该含义。
卡洛·伍德

@bstpierre:这里的重点不是人们是否可以合理化=~匹配空字符串的行为。这是因为此行为可能是意外的,并且可能导致错误。我之所以写这个答案,是因为我被它烧死了。
Penghe Geng

@PengheGeng意外行为?如果一个模式没有定义或约束,那么它实际上可以匹配任何东西。模式的缺失是所有事物的匹配。编写健壮的代码才是答案,而不是给出错误的解释。
Anthony Rutledge

@AnthonyRutledge“健壮的代码”要求最好地使用可用的工具,以防止意外的编码错误。在Shell代码中,可以通过拼写错误等方式随时随地轻松地引入空变量,我不认为允许对空变量进行匹配是一项强大的功能。显然,GNU的作者expr同意我的观点。
澎河耕

0

如果使用正则表达式有助于确定日期的字符序列是否正确,则不能轻易使用它来确定日期是否有效。以下示例将传递正则表达式,但所有日期均无效: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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.