将日期时间格式与Bash REGEX匹配

时间:2014-02-20 14:07:01

标签: regex bash datetime

我在bash中有这种日期时间格式的数据:

28/11/13 06:20:05(dd / mm / yy hh:mm:ss)

我需要重新格式化:

2013-11-28 06:20:05(MySQL日期时间格式)

我正在使用以下正则表达式:

regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9])\s([0-9][0-9]/:[0-9][0-9]:[0-9][0-9])'

if [[$line=~$regex]]
then
   $line='20$3-$2-$1 $4';
fi

这会产生错误:

./filename: line 10: [[09:34:38=~([0-9][0-9])/([0-9][0-9])/([0-9][0-9])\s([0-9][0-9]/:[0-9][0-9]:[0-9][0-9])]]: No such file or directory

更新:

我想“逐行”读取此文件,解析它并在mysql数据库中插入数据:

'filenameX':

27/11/13 12:20:05 9984 2885 260 54 288 94 696 1852 32 88 27 7 154
27/11/13 13:20:05 9978 2886 262 54 287 93 696 1854 32 88 27 7 154
27/11/13 14:20:05 9955 2875 262 54 287 93 696 1860 32 88 27 7 154
27/11/13 15:20:04 9921 2874 261 54 284 93 692 1868 32 88 27 7 154
27/11/13 16:20:09 9896 2864 260 54 283 92 689 1880 32 88 27 7 154
27/11/13 17:20:05 9858 2858 258 54 279 92 683 1888 32 88 27 7 154
27/11/13 18:20:04 9849 2853 258 54 279 92 683 1891 32 88 27 7 154
27/11/13 19:20:04 9836 2850 257 54 279 93 683 1891 32 88 27 7 154
27/11/13 20:20:05 9826 2845 257 54 279 93 683 1892 32 88 27 7 154
27/11/13 21:20:05 9820 2847 257 54 278 93 682 1892 32 88 27 7 154
27/11/13 22:20:04 9810 2844 257 54 277 93 681 1892 32 88 27 7 154
27/11/13 23:20:04 9807 2843 257 54 276 93 680 1892 32 88 27 7 154
28/11/13 00:20:05 9809 2843 257 54 276 93 680 1747 29 87 17 6 139
28/11/13 01:20:04 9809 2842 257 54 276 93 680 1747 29 87 17 6 139
28/11/13 02:20:05 9809 2843 256 54 276 93 679 1747 29 87 17 6 139
28/11/13 03:20:04 9808 2842 256 54 276 93 679 1747 29 87 17 6 139
28/11/13 04:20:05 9808 2842 256 54 276 93 679 1747 29 87 17 6 139
28/11/13 05:20:39 9807 2842 256 54 276 93 679 1747 29 87 17 6 139
28/11/13 06:20:05 9804 2840 256 54 276 93 679 1747 29 87 17 6 139

脚本:

#!/bin/bash

echo "Start!"

while IFS='     ' read -ra ADDR;
do
   for line in $(cat results)
   do
      regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9]) ([0-9][0-9]:[0-9][0-9]:[0-9]$
      if [[ $line =~ $regex ]]; then
         $line="20${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]} ${BASH_REMATCH[4]}"
      fi
      echo "insert into table(time, total, caracas, anzoategui) values('$line', '$line', '$line', '$line', '$line');"
   done | mysql -user -password database;
done < filenameX

结果:

时间|总计|加拉加斯| anzoategui |
00:00:00 | 9 | 9 | 9 |
2027-11-13 00:00:00 | 15 | 15 | 15 |

5 个答案:

答案 0 :(得分:2)

注意:此答案是基于在OP中修复以bash为重点的方法而被接受的。对于更简单的基于awk的解决方案,请参阅此答案的最后一部分。

尝试以下方法:

line='28/11/13 06:20:05' # sample input

regex='([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 [[ $line =~ $regex ]]; then
  line="20${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]} ${BASH_REMATCH[4]}"
fi

echo "$line"  # -> '2013-11-28 06:20:05'

至于为什么你的代码不起作用:

  • 正如@anubhava指出的那样,[[右侧和]]左侧至少需要一个空格。
  • \s在bash正则表达式中是否有效取决于平台(Linux:是; OSX:否),因此单个文字空间是更安全的选择。
  • 您的变量分配不正确($line = ...) - 当分配给变量时,永远不要在变量名前加上$
  • 您的反向引用不正确($1,...):要在bash正则表达式中引用捕获组(子表达式),您必须使用特殊的${BASH_REMATCH[@]}数组变量; ${BASH_REMATCH[0]}包含匹配的整个字符串,${BASH_REMATCH[1]}包含第一个捕获组匹配的内容,依此类推;相比之下,$1$2,...是指传递给shell脚本或函数的第1个,第2个......参数。

更新,以解决OP的更新问题:

认为以下是你想要的:

# Read input file and store each col. value in separate variables.
while read -r f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15; do

    # Concatenate the first 2 cols. to form a date + time string.
    dt="$f1 $f2"

    # Parse and reformat the date + time string.
    regex='([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 [[ "$dt" =~ $regex ]]; then
      dt="20${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]} ${BASH_REMATCH[4]}"
    fi

    # Echo the SQL command; all of them are piped into a `mysql` command
    # at the end of the loop.
    # !! Fill the $f<n> variables in as needed - I don't know which ones you need.
    # !! Make sure the number column name matches the number of values.
    # !! Your original code had 4 column names, but 5 values, causing an error.
    echo "insert into table(time, total, caracas, anzoategui) values('$dt', '$f3', '$f4', '$f5');"

done < filenameX | mysql -user -password database

事后补充:上述解决方案基于对OP代码的改进;下面是一个简化的解决方案,它是一个基于awk的单行程序(为了便于阅读而分布在多行中 - 对于基于awk的日期重新格式化的@twalberg,提示:)

awk -v sq=\' '{
 split($1, tkns, "/");
 dt=sprintf("20%s-%s-%s", tkns[3], tkns[2], tkns[1]); 
 printf "insert into table(time,total,caracas,anzoategui) values(%s,%s,%s,%s);", 
   sq dt " " $2 sq, sq $3 sq, sq $4 sq, sq $5 sq
}' filenameX | mysql -user -password database

注意:要简化awk程序中的引用,可以通过变量sq-v sq=\')传入单引号。

答案 1 :(得分:1)

Perl在这里很方便。

dt="28/11/13 06:20:05"
perl -MTime::Piece -E "say Time::Piece->strptime('$dt', '%d/%m/%y %T')->strftime('%Y-%m-%d %T')"
2013-11-28 06:20:05

答案 2 :(得分:1)

这样做没有任何过于复杂的正则表达式调用:

echo "28/11/13 06:20:05" | awk -F'[/ ]' \
    '{printf "20%s-%s-%s %s\n", $3, $2, $1, $4}'

或者,正如@fedorqui在评论中所建议的那样,如果时间戳的来源为date,您可以为其提供所需的格式选项...

答案 3 :(得分:0)

BASH中必须使用空格,因此请使用:

[[ "$line" =~ $regex ]] && echo "${line//\//-}"

此外,您无法在BASH中使用\s,因此请使用此正则表达式:

regex='([0-9][0-9])/([0-9][0-9])/([0-9][0-9]) ([0-9][0-9]:[0-9][0-9]:[0-9][0-9])'

答案 4 :(得分:0)

感谢所有上述示例。

未附加“ T”

$line='"2020-11-26 10:20:01.000000","the size of the table is 3.5" (inches)","2020-12-11 10:20:02"'
$echo "$line" | sed -r 's#(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})#\2T\1#g'
"2020-11-26 10:20:01.000000","the size of the table is 3.5" (inches)","2020-12-11 10:20:02"

“ T”仅附加在第一列的中间,而不附加在该行中任何具有日期格式的列中

$awk '/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]*/{print}' test_file |sed -e 's/\s/\T/'
"2020-11-26T10:20:01.000000","the size of the table is 3.5" (inches)","2020-12-11 10:20:02"

上面带有分组的示例

$ line='"2020-11-26 10:20:01.000000","the size of the table is 3.5" (inches)","2020-12-11 10:20:02"'
$ regex='([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 [[ $line =~ $regex ]]; then line="20${BASH_REMATCH[3]}-${BASH_REMATCH[2]}-${BASH_REMATCH[1]}T${BASH_REMATCH[4]}"; fi
$ echo "$line" 
2026-11-20T10:20:01

#......的意图是在具有数百万条记录的巨大csv文件中的所有字段的日期和时间(同一字段)之间添加“ T”,而不仅仅是第一列,且所有日期格式都为YYYY-MM-DD HH24:MI:SS