【问题标题】:replace multiple lines identifying end character替换标识结束字符的多行
【发布时间】:2015-03-24 13:51:25
【问题描述】:

我有下面的代码

CREATE TABLE Table1(
        column1 double NOT NULL,
        column2 varchar(60) NULL,
        column3 varchar(60) NULL,
        column4 double NOT NULL,
 CONSTRAINT Index1 PRIMARY KEY CLUSTERED
(
        column2 ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON PRIMARY
) ON PRIMARY

GO
GO

我想替换

 CONSTRAINT Index1 PRIMARY KEY CLUSTERED
(
        column2 ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON PRIMARY
) ON PRIMARY

GO

)

您不能假设 GO 是文件的最后一个字符。 Go 之后可以有另一个表脚本。 如何使用单个 sed 或 awk 做到这一点。

【问题讨论】:

  • 一如既往,请发布您尝试过的内容...

标签: shell awk sed


【解决方案1】:

更新:

您甚至可以使用以下sed 命令替换CONSTRAINT 块之前的最后一个,

sed -r '/,/{N;/CONSTRAINT/{:a;N;/GO/!ba;s/([^,]+).*/\1\n)/};/CONSTRAINT/!n}' input.sql

让我把它解释为一个多行脚本:

# Search for a comma
/,/ {
  # If a command was found slurp in the next line
  # and append it to the current line in pattern buffer
  N
  # If the pattern buffer does not contain the word CONSTRAINT
  # print the pattern buffer and go on with the next line of input
  # meaning start searching for a comma
  /CONSTRAINT/! n

  # If the pattern CONSTRAINT was found we loop until we find the 
  # word GO
  /CONSTRAINT/ {
    # Define a start label for the loop 
    :a
    # Append the next line of input to the pattern buffer
    N
    # If GO is still not found in the pattern buffern
    # step to the start label of the loop
    /GO/! ba

    # The loop was exited meaning the pattern GO was found.
    # We keep the first line of the pattern buffer - without
    # the comma at the end and replace everything else by a )
    s/([^,]+).*/\1\n)/
  }
}

您可以将上面的多行脚本保存在一个文件中并使用它执行它

sed -rf script.sed input.sql

您可以使用以下sed 命令:

sed '/CONSTRAINT/{:a;N;/GO/!ba;s/.*/)/}' input.sql

该模式搜索包含/CONSTRAINT/ 的行。如果找到该模式,则开始在{ } 之间包裹一个命令块。在块中,我们首先定义一个标签a:a。我们通过N 获得下一行输入并将其附加到模式缓冲区。除非我们找到模式/GO/!,否则我们将使用分支命令b 在标签a 处继续。如果找到/GO/ 模式,我们只需将缓冲区替换为)


另一种方法是使用 FredPhil 建议的范围:

sed '/CONSTRAINT/,/GO/{s/GO/)/;te;d;:e}'

【讨论】:

  • 无需重复GO 正则表达式; //s/.*/)/ 就够了。 (// 再次尝试最后一个正则表达式)
  • 感谢您的回复。我想从最后一个逗号开始替换。意味着我也想替换最后一个逗号。也请您提供我可以阅读有关使用标签和缓冲区进行搜索和替换的链接
  • @Koushik 我真的很喜欢这个教程:grymoire.com/Unix/Sed.html。让我更改答案以替换逗号。
  • @hek2mgl 我说对了一半,而你找到了另一半。当然,完全没有必要再次测试正则表达式!很好发现。
【解决方案2】:

这可能看起来很吓人,但稍微解释一下就不难理解了:

SED_DELIM=$(echo -en "\001")
START=' CONSTRAINT Index1 PRIMARY KEY CLUSTERED'
END='GO' 
sed -n $'\x5c'"${SED_DELIM}${START}${SED_DELIM},"$'\x5c'"${SED_DELIM}${END}${SED_DELIM}{s${SED_DELIM}GO${SED_DELIM})${SED_DELIM};t a;d;:a;};p" test2.txt

sed有以下形式你可能比较熟悉:
sed /regex1/,/regex2/{commands}

首先它使用 SOH 不可打印作为分隔符\001
为 sed 多行匹配设置 START 和 END 标签
然后执行 sed 命令:
-n 默认不打印
$'\x5c' 是一个 Bash 字符串文字,对应于反斜杠 \
反斜杠对于在多行范围匹配中转义不可打印的分隔符是必需的。
{s${SED_DELIM}GO${SED_DELIM})${SED_DELIM};t a;d;:a;};p:
s${SED_DELIM}GO${SED_DELIM})${SED_DELIM} 将匹配 GO 的行替换为 )
t a; 如果存在在前面的语句中成功替换然后分支到:a 标签
d 如果没有替换然后删除该行
p 打印命令之后的任何结果 分支到

在发布之前我没有看到他们的答案 - 这个答案与 FredPhil/hek2mgl 相同 - 除了通过这种方式你有一个机制可以在 LHS 上更加动态,因为你可以将分隔符更改为一个字符出现在数据集中的可能性要小得多。

【讨论】:

    【解决方案3】:

    对于多字符 RS 使用 GNU awk 并假设您想去掉“CONSTRAINT”之前的逗号:

    $ cat tst.awk
    BEGIN{ RS="^$"; ORS="" }
    {
        gsub(/\<GO\>/,"\034")
        gsub(/,\s*CONSTRAINT[^\034]+\034/,")")
        gsub(/\034/,"GO")
        print
    }
    $ gawk -f tst.awk file
    CREATE TABLE Table1(
            column1 double NOT NULL,
            column2 varchar(60) NULL,
            column3 varchar(60) NULL,
            column4 double NOT NULL)
    GO
    

    上述方法通过将每个独立的“GO”替换为不太可能出现在您的输入中的控制字符(在这种情况下,我使用与默认 SUBSEP 相同的值),因此我们可以在否定字符中使用该字符中间的列表 gsub() 以创建一个以“CONSTRAINT”之后的第一个“GO”结尾的正则表达式。这是在 awk 中进行“非贪婪”匹配的一种方法。

    如果没有你知道的字符不能出现在你的输入中,你可以像这样创建一个:

    $ cat tst.awk
    BEGIN{ RS="^$"; ORS="" }
    {
        gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/\<GO\>/,"b")
        gsub(/,\s*CONSTRAINT[^b]+b/,")")
        gsub(/b/,"GO"); gsub(/aB/,"b"); gsub(/aA/,"a")
        print
    }
    $ 
    $ gawk -f tst.awk file
    CREATE TABLE Table1(
            column1 double NOT NULL,
            column2 varchar(60) NULL,
            column3 varchar(60) NULL,
            column4 double NOT NULL)
    GO
    

    上面最初将所有“a”转换为“aA”,将“b”转换为“aB”,这样

    1. 记录中不再有任何“b”,并且
    2. 由于所有原始的“a”现在后面都有一个“A”,因此唯一出现的 “aB”表示“bs”最初所在的位置

    这意味着我们现在可以将所有“GO”转换为“b”,就像我们在上面的第一个脚本中将它们转换为“\034”一样。然后我们执行主 gsub(),然后展开我们的初始 gsub()。

    gsub()ing 的想法是创建以前不存在的字符,使用这些字符,然后展开初始的 gsub()s 是一个非常有用的学习和记忆习语,例如有关其他应用程序,请参阅 https://stackoverflow.com/a/13062682/1745001

    一步一步地看它工作:

    $ cat file                                                                                                   
    foo bar Hello World World able bodies
    
    $ awk '{gsub(/a/,"aA")}1' file                                                                               
    foo baAr Hello World World aAble bodies
    
    $ awk '{gsub(/a/,"aA"); gsub(/b/,"aB")}1' file                                                               
    foo aBaAr Hello World World aAaBle aBodies
    
    $ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b")}1' file                                            
    foo aBaAr Hello b b aAaBle aBodies
    
    $ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The")}1' file                         
    foo aBaAr We Are The b aAaBle aBodies
    
    $ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The"); gsub(/b/,"World")}1' file 
    foo aBaAr We Are The World aAaBle aBodies
    
    $ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The"); gsub(/b/,"World"); gsub(/aB/,"b")}1' file
    foo baAr We Are The World aAble bodies
    
    $ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The"); gsub(/b/,"World"); gsub(/aB/,"b"); ; gsub(/aA/,"a")}1' file
    foo bar We Are The World able bodies
    

    【讨论】:

    • 我不确定 OP 是否真的想用硬编码的内容替换 CONSTRAINT。可能他想替换所有表定义中的每个CONSTRAINT 定义。
    • 我可以想象这样一种情况,您希望将数据库转储从支持约束的数据库处理到不支持约束的数据库中。但是,这并不能解释 GO 语句。也许您可以概括awk 命令,希望看到您的尝试.. +1 为awk 替代方案无论如何...(虽然我认为sed 代码在这种情况下相对容易编写和维护)
    • 不完全是。您还需要从带有CONSTRAINT 的行之前的行中删除,,然后将包括第一个GO 在内的所有以下行替换为单个)。我用了一个循环。
    • 是的,看看我的答案下面的 cmets。 (否则会出现SQL语法错误)...(注意我需要AFK,后面会看这里)
    • 好的,我更新了我的答案,只删除了 , CONSTRAINT 和之后的第一个 GO 之间的任何内容。
    猜你喜欢
    • 2017-08-22
    • 2022-07-07
    • 2021-03-16
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 2014-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多