BASH 不喜欢我的正则表达式

BASH 不喜欢我的正则表达式

我试图获取文件修改的 2 位数月份和 2 位数年份,但它不起作用。

modified=$(stat -c %y "$line"); 
# modified="2018-08-22 14:39:36.400469308 -0400"
if [[ $modified =~ ".{2}(\d{2})-(\d{2})" ]]; then
    echo ${BASH_REMATCH[0]}
    echo ${BASH_REMATCH[1]
fi

我究竟做错了什么?

答案1

首先,引号抑制了正则表达式中特殊字符的含义(在线手册):

可以使用附加的二元运算符=~, ... 可以引用模式的任何部分,以强制将引用的部分作为字符串进行匹配。 ...如果要匹配正则表达式语法中特殊的字符,则必须将其加引号以删除其特殊含义。

该手册继续建议将正则表达式放入变量中,以防止 shell 解析和正则表达式语法之间发生一些冲突。

其次,\d不做你认为它做的事情,而只是匹配文字d

另请注意,${BASH_REMATCH[0]}包含整个匹配字符串,索引1和向上包含捕获的组。

我还强烈建议使用四位数年份,因此:

modified=$(stat -c %y "$file")
re='^([0-9]{4})-([0-9]{2})'
if [[ $modified =~ $re ]]; then
    echo "year:  ${BASH_REMATCH[1]}"
    echo "month: ${BASH_REMATCH[2]}"
else
    echo "invalid timestamp"
fi

对于今天修改的文件,给出year: 2018month: 08。请注意,带有前导零的数字将被 shell 和可能的其他实用程序视为八进制。

(如果您需要处理 1900 年代的日期,四位数年份的问题较少,而且它们更容易识别为年份而不是月份中的日期。)

答案2

为此不需要正则表达式:

$ touch -t 197001010000 myfile
$ ls -l myfile
-rw-rw-r-- 1 jackman jackman 0 Jan  1  1970 myfile
$ IFS='-' read -r year month _rest < <(stat -c %y myfile)
$ echo "$year:${year#??}"$month"
1970:70:01

答案3

作为替代方案,使用 GNU date,您可以执行以下操作:

eval "$(date -r "$file" +'year=%Y month=%-m day=%-d')"

要将修改时间的年、月和日部分分别存储在$year$month和 中$day(作为十进制整数,如果您关心前导零,请删除 和 中的 s;另请参阅2-位年份)。%-m%-d%y

(请注意,与 GNU 相反stat,对于符号链接类型的文件,会考虑符号链接目标的修改时间,而不是符号链接本身的修改时间。使用 GNU 时stat,您可以使用stat -L)。

相关内容