在两个标记字符串

时间:2017-07-14 20:11:46

标签: regex perl awk sed command-line

与正则表达式模式并非如此,因为它是如何来实现它。我尝试过perl,sed和awk(各自尝试各种尝试),但我不确定这有多可能是一个单行(我更喜欢不编写perl脚本)。

说我有

#MARKER_TOP
INSERT INTO ('col1', 'col2', 'col3')
VALUES
(123,123,'2018-20-20 24:24:24',123)
...etc.
(123,123,'2018-20-20 24:24:24',123);
#MARKER_BOTTOM

...and more! (not all INSERT tables will be marked, btw)

我想要做的是用SQL NOW()替换所有字符串日期。具体来说,使用Perl,我尝试了以下内容:

perl -w -pi.bak -e "undef $/; s/(#MARKER_TOP.*)'[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]'(.*#MARKER_BOTTOM)/$1 NOW() $2/msg" test.sql

但它正在完全剥离所有感兴趣的块(#MARKER_TOP等)并用NOW()替换它,这对于我想要的东西来说太过苛刻。

2 个答案:

答案 0 :(得分:3)

一种简单的方法是使用range operator

use warnings;
use strict;

my $file = 'test.sql';
open my $fh, '<', $file or die "Can't open $file: $!";

my $re_date = qr/'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'/;

while (<$fh>) 
{
    if (/#MARKER_TOP/ .. /#MARKER_BOTTOM/) {
        s/$re_date/NOW()/;
    }   

    print;
}

或单行

perl -wpe"s/'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'/NOW()/ 
    if /#MARKER_TOP/ .. /#MARKER_BOTTOM/
" test.sql

我用"..."分隔代码,以便能够在内部使用'(还有其他方法)。

我假设一行中有一个日期,严格按照问题中给出的格式在''内。我通过添加另一个INSERT部分进行测试,但没有标记。替换只发生在标记括号中。

范围运算符通过保持状态来工作:一旦其左操作数变为true,它就变为true,并且在右操作数变为true之前保持为真,之后在下一次迭代中返回false。它在标量上下文中以这种方式工作,而在列表上下文中,它返回该范围内的列表。请参阅链接的文档。

答案 1 :(得分:2)

您可以像这样使用awk

awk '/#MARKER_TOP/{m=1} /#MARKER_BOTTOM/{m=0} m{
   gsub(/\047[0-9]{4}(-[0-9]{2}){2} [0-9]{2}(:[0-9]{2}){2}\047/, "NOW()")} 1' file

#MARKER_TOP
INSERT INTO ('col1', 'col2', 'col3')
VALUES
(123,123,NOW(),123)
...etc.
(123,123,NOW(),123);
#MARKER_BOTTOM

以下是它的工作原理:

  • /#MARKER_TOP/{m=1}:当我们收到短信m=1时设置标记#MARKER_TOP
  • /#MARKER_BOTTOM/{m=0}:当我们收到短信m=0
  • 时,重置标记#MARKER_BOTTOM
  • m{gsub(/.../, "NOW()")}m==1时使用正则表达式将日期字符串替换为NOW()
  • 1:打印每条记录