替换识别结束字符的多行

时间:2015-03-24 13:51:26

标签: shell awk sed

我有以下代码

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做到这一点。

3 个答案:

答案 0 :(得分:2)

<强>更新

您可以使用以下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/!,否则我们将使用分支命令a继续标记b。如果找到模式/GO/,我们只需用)替换缓冲区。


替代方案可以使用像FredPhil建议的范围:

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

答案 1 :(得分:1)

使用GNU awk进行多字符RS并假设您想要在&#34; CONSTRAINT&#34;之前删除逗号:

$ 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

上述工作取代了每一个单独的&#34; GO&#34;使用控件char不太可能出现在您的输入中(在这种情况下,我使用与默认SUBSEP相同的值),因此我们可以在中间gsub()中的否定字符列表中使用该char来创建regexp结束于第一个&#34; GO&#34;在&#34; CONSTRAINT&#34;之后。这是一种做法&#34;非贪婪&#34;在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

以上最初将所有&#34; a&#34; s转换为&#34; aA&#34;和&#34; b&#34; s到&#34; aB&#34;这样

  1. 记录中不再有任何&#34; b&#34;和
  2. 因为所有原创的&#34; a&#34;现在有一个&#34; A&#34;在他们之后,唯一的出现 &#34; AB&#34;代表&#34; bs&#34;最初位于
  3. 这意味着我们现在可以将所有&#34; GO&#34; s转换为&#34; b&#34; s就像我们将它们转换为&#34; \ 034&#34;在上面的第一个脚本中。然后我们做主gsub(),然后展开我们的初始gsub()s。

    {{1}创建使用这些字符以前无法存在的字符,然后展开初始gsub()的这个想法是一个非常有用的学习和记忆习惯,例如,请参阅https://stackoverflow.com/a/13062682/1745001了解其他申请。

    要看到它一次只能工作一步:

    gsub()

答案 2 :(得分:1)

这可能看起来很可怕,但通过一些解释并不难理解:

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上更加动态,因为你可以将分隔符更改为不太可能出现在数据集中的字符。