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;
这只是稍微简化了语法。
我想在 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;
这只是稍微简化了语法。