awk(或 sed,或其他东西) - 引用匹配上一行并添加一行

awk (or sed, or something else) - reference match on a previous line and add a line

我想在 MySQL 数据库(超过 50 个存储过程)中的每个存储过程的第一行添加一个过程调用,将执行例程的名称传递给新调用。

在 MySQL 中没有简单的方法可以解决这个问题,所以我想我会尝试使用 MySQLdump 导出例程,编辑文件awk/sed/something 然后重新创建 SP。事实证明比我预期的要棘手。

这涉及从 CREATE ... PROCEDURE 行中查找函数名称,然后在下一个 BEGIN 语句之后添加 CALL NewFunc(currentFuncName) 行。重要的是,下一个 BEGIN 语句在 CREATE 之后的行数并不总是相同。

我正在途中,我只是想不出如何把这两个位联系起来。我可以用这个得到函数名:

awk '/CREATE.*PROCEDURE/ {gsub(/^.*PROCEDURE `|`\(.*$/,"");print "call newFuncHere("  ");"}'

如何让 awk 在下一个 BEGIN 语句 之后添加此 的结果?

示例来源:

CREATE DEFINER=`blah@%` PROCEDURE `theProcedure`(`param1` BIGINT(21) UNSIGNED,
theExisting BIGINT(21) UNSIGNED,
INOUT `myStatus` VARCHAR(255) )
SQL SECURITY INVOKER
BEGIN
    DECLARE blah VARCHAR(32) DEFAULT NULL;

示例输出:

CREATE DEFINER=`blah@%` PROCEDURE `theProcedure`(`param1` BIGINT(21) UNSIGNED,
theExisting BIGINT(21) UNSIGNED,
INOUT `myStatus` VARCHAR(255) )
SQL SECURITY INVOKER
BEGIN
    CALL newFuncHere(theProcedure);
    DECLARE blah VARCHAR(32) DEFAULT NULL;

缩进不相关 - 仅作为示例显示


编辑添加,忘记了 DECLARE 语句必须先出现的要求,所以这就是我最终得到的...

# Initialise variables
BEGIN { seen=0; beginCount=-1; createCount=-1; done=0; }

# Ignore blank lines but still print them out
/^$|^\t$/ { print [=13=] ; next ;}

# If matches CREATE.*PROCEDURE|FUNCTION - increase createCount and set name of function/beginCount variables
/^CREATE/{createCount++} (~/PROCEDURE/ || ~/FUNCTION/)  { name= ; beginCount=-1 }

# If BEGIN is found - increase beginCount - this is to count the depth of BEGIN statements
/BEGIN/ {beginCount++}

# Increment seen variable if DECLARE is in this line
/DECLARE/{seen++}

# If beginCounter = 0, we're at BEGIN level 0 (starts at -1) and if done is not set we've not yet done anything. A
# create depth of 0 (starts at -1), and we've seen at least one DECLARE line,
# if this line does NOT contain DECLARE, add the new line we want, followed
# by a newline and the current line. Reset some variables.
!done && !beginCount && !createCount && seen && !/DECLARE/{ print "CALL newFunction('" name "');" ORS [=13=] ; seen=0;count++; done=1 ; next;}

#{print createCount seen beginCount done}

# If END is seen and we're inside a nested BEGIN statement, decrement beginCount
# print out the line and skip to the next line
/END.*;/ && beginCount {beginCount--; print [=13=] ; next ;}

# If END is seen and we're at the top level, reset some counters
/END.*;/ && !beginCount {createCount=-1 ; beginCount=-1 ; seen=0; done=0; }

# print out the line
{ print [=13=] ;}

它并不完美,它需要处理 END IF 语句,不处理 DECLARE 语句覆盖多行的情况,还有一些其他情况它不起作用,并且其中有一些多余的东西以前的实验 - 但它已经足够满足我现在的需要了。

假设:

  • 当前过程名称是反引号分隔行中的字段 #4
  • 新行的缩进与 BEGIN 相同,再加上 3 个字符(例如,示例总共 4 个字符)

示例输入:

$ cat ddl.sql
CREATE DEFINER=`blah@%` PROCEDURE `theProcedure`(`param1` BIGINT(21) UNSIGNED,
theExisting BIGINT(21) UNSIGNED,
INOUT `myStatus` VARCHAR(255) )
SQL SECURITY INVOKER
BEGIN
    DECLARE blah VARCHAR(32) DEFAULT NULL;
    BEGIN
    ....
 ....
..
BEGIN
BEGIN
END

一个awk想法:

awk -F'`' '
procname && /BEGIN/ { print
                      n=index([=11=],"BEGIN")+3                                  # find indentation of "BEGIN" and +3
                      printf "%*scall newFuncHere(%s);\n", n, "", procname   # indent by "n" spaces
                      procname=""
                      next
                    }
/CREATE.*PROCEDURE/ { procname= }
1
' ddl.sql

这会生成:

CREATE DEFINER=`blah@%` PROCEDURE `theProcedure`(`param1` BIGINT(21) UNSIGNED,
theExisting BIGINT(21) UNSIGNED,
INOUT `myStatus` VARCHAR(255) )
SQL SECURITY INVOKER
BEGIN
    call newFuncHere(theProcedure);
    DECLARE blah VARCHAR(32) DEFAULT NULL;
    BEGIN
    ....
 ....
..
BEGIN
BEGIN
END

@markp-fuso 的答案略有不同,它依赖 PROCEDURE 出现在包含函数名称的行中作为第 3 个字段作为第 4 个字段,然后依赖 BEGIN 出现在您要在其后插入新函数调用的行的开头可以是:

awk -F'`' '
    /^CREATE/ && ~/PROCEDURE/  { name= }
    /^BEGIN/  { print [=10=] ORS "    CALL newFuncHere(" name ");"; next }
              1
' file

上面有3条规则:

  • /^CREATE/ && ~/PROCEDURE/为条件的第一个简单地将函数名保存为name
  • 第二个条件 /^BEGIN/ 简单地输出当前记录和新的函数调用并跳到下一条记录(注意: 这假定总会有一个每个 BEGIN 行之前的函数名称——如果没有,设置和取消设置名称并添加 name 作为条件);
  • 最终规则 1 只是 shorthand 默认 print 命令输出当前记录不变。

例子Use/Output

使用文件 file 中的示例数据,您可以简单地 copy/middle-mouse 将以上内容粘贴到当前工作目录中带有 file 的 xterm 中并接收:

$ awk -F'`' '
>     /^CREATE/ && ~/PROCEDURE/  { name= }
>     /^BEGIN/  { print [=11=] ORS "    CALL newFuncHere(" name ");"; next }
>               1
> ' f
CREATE DEFINER=`blah@%` PROCEDURE `theProcedure`(`param1` BIGINT(21) UNSIGNED,
theExisting BIGINT(21) UNSIGNED,
INOUT `myStatus` VARCHAR(255) )
SQL SECURITY INVOKER
BEGIN
    CALL newFuncHere(theProcedure);
    DECLARE blah VARCHAR(32) DEFAULT NULL;

这只是稍微简化了语法。