【问题标题】:Removing a line, plus drop the comma of the last line with a matching pattern删除一行,加上匹配模式的最后一行的逗号
【发布时间】:2019-07-20 01:46:45
【问题描述】:
DROP TABLE IF EXISTS `qalnk`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `qalnk` (
  `id` bigint(20) NOT NULL,
  `answer_id` bigint(20) DEFAULT NULL,
  `date_deleted` bigint(20) DEFAULT NULL,
  `deleted_by_user_ap_id` varchar(36) DEFAULT NULL,
  `expression_id` bigint(20) DEFAULT NULL,
  `expression_type` varchar(255) DEFAULT NULL,
  `ordering` int(11) DEFAULT NULL,
  `question_id` bigint(20) DEFAULT NULL,
  `expression_for_deselect_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK6661B19F393DFCD` (`expression_id`),
  KEY `FK6661B195182DDCD` (`question_id`),
  KEY `FK6661B195742A56B` (`expression_for_deselect_id`),
  KEY `idx_qlnk_nswrd` (`answer_id`),
  KEY `FK6661B19126D878D` (`answer_id`),
  KEY `FK6661B1975B33071` (`id`),
  CONSTRAINT `FK6661B19126D878D` FOREIGN KEY (`answer_id`) REFERENCES `ans` (`id`),
  CONSTRAINT `FK6661B1975B33071` FOREIGN KEY (`id`) REFERENCES `apobj` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

我的目标是从数据库中删除所有约束,所以我想对单词“CONSTRAINT”进行简单的代码搜索并删除该行

我尝试使用 sed

sed '/\s*CONSTRAINT/d' ~/Downloads/dump.sql > ~/ouput.sql

但是由于 CONSTRAINTS 是最后一个语句,所有这些尾随逗号都被留下了。我不介意它是 awk、sed 还是一些常用工具。

想要的输出是

DROP TABLE IF EXISTS `qalnk`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `qalnk` (
  `id` bigint(20) NOT NULL,
  `answer_id` bigint(20) DEFAULT NULL,
  `date_deleted` bigint(20) DEFAULT NULL,
  `deleted_by_user_ap_id` varchar(36) DEFAULT NULL,
  `expression_id` bigint(20) DEFAULT NULL,
  `expression_type` varchar(255) DEFAULT NULL,
  `ordering` int(11) DEFAULT NULL,
  `question_id` bigint(20) DEFAULT NULL,
  `expression_for_deselect_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK6661B19F393DFCD` (`expression_id`),
  KEY `FK6661B195182DDCD` (`question_id`),
  KEY `FK6661B195742A56B` (`expression_for_deselect_id`),
  KEY `idx_qlnk_nswrd` (`answer_id`),
  KEY `FK6661B19126D878D` (`answer_id`),
  KEY `FK6661B1975B33071` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

【问题讨论】:

  • 对你的解决方案稍作修改:sed '/CONSTRAINT/d' ~/Downloads/dump.sql > ~/ouput.sql
  • @josifoski:虽然您的建议确实是 OP 的 sed 命令的更简单(非冗余)等效,但在这种情况下,更好的选择可能是 sed '/^\s*CONSTRAINT /d' - 即,anchor 行首的表达式(请注意,\s 的使用假定 GNU sed; 与 mandatory 前面的空格你需要sed '/^\s\+CONSTRAINT /d')。

标签: awk sed


【解决方案1】:

sed 是在单行上进行简单替换的出色工具,但对于其他任何事情,只需使用 awk。这是多字符 RS 的 GNU awk 和 [[:space:]] 的缩写 \s

$ awk -v RS='^$' -v ORS= '{gsub(/,\s*CONSTRAINT[^\n,]+/,"")}1' file
DROP TABLE IF EXISTS `qalnk`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `qalnk` (
  `id` bigint(20) NOT NULL,
  `answer_id` bigint(20) DEFAULT NULL,
  `date_deleted` bigint(20) DEFAULT NULL,
  `deleted_by_user_ap_id` varchar(36) DEFAULT NULL,
  `expression_id` bigint(20) DEFAULT NULL,
  `expression_type` varchar(255) DEFAULT NULL,
  `ordering` int(11) DEFAULT NULL,
  `question_id` bigint(20) DEFAULT NULL,
  `expression_for_deselect_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK6661B19F393DFCD` (`expression_id`),
  KEY `FK6661B195182DDCD` (`question_id`),
  KEY `FK6661B195742A56B` (`expression_for_deselect_id`),
  KEY `idx_qlnk_nswrd` (`answer_id`),
  KEY `FK6661B19126D878D` (`answer_id`),
  KEY `FK6661B1975B33071` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

对于仅限 POSIX 的解决方案(请参阅下面来自 @mklement0 的 cmets):

awk -v RS=$(printf '\3') -v ORS= '{gsub(/,[[:space:]]*CONSTRAINT[^\n,]+/,"")}1'

【讨论】:

  • 不错;这确实比sed 解决方案更简单;但是,要使其符合 POSIX 并使其在 BWK awk 上工作(因为我们现在知道 OP 在 OSX 上),请使用 awk -v RS= '{gsub(/,[[:space:]]*CONSTRAINT[^\n,]+/,"")}1' - 即,不要将正则表达式用作 RS(依靠empty RS 将连续行块视为单个记录),并使用 [[:space:]] 代替 \s
  • 是的,如果块内没有空行并且 OP 不介意多个空行被截断,这将起作用。在非 gawk 中执行此操作的典型方法是执行 {rec = (rec ? rec ORS : "") $0} END{gsub(...,rec); print rec}',因此您在阅读时一次建立一行记录,然后在 END 部分执行您想要的操作。您也可以只使用您知道输入文件中不存在的控制字符作为 RS。 BEGIN{RS=SUBSEP} 通常是一种可接受的方法。
  • 使用-v RS='^$' 或等效项时的常用方法只是指定-v ORS=,因此打印不会添加任何内容:awk -v RS=$(printf '\3') -v ORS= '{gsub(/,[[:space:]]*CONSTRAINT[^\n,]+/,"")}1'
  • 完成。不过我不认为这是一个技巧,它只是清楚地指定了你想在每条记录的末尾输出什么——默认值(在阅读RS='\n' 提供的输入时去掉换行符)是你想要在打印输出时添加换行符,但在这种情况下,您在读取记录时不会从记录中删除任何内容,因此您不想在打印时添加任何内容。
  • 谢谢,谢谢你教我一两件事。你是对的:这不是一个把戏,但它的简单性让我这么称呼它。如果我们对 collect-all-lines-one-by-one-approach 应用相同的方法,我们会得到:awk -v ORS= '{ rec = rec $0 RS } END { print rec }'(请注意,我已将字符串连接从 ORS 切换到 RS)。跨度>
【解决方案2】:

这有点小技巧 - 使用 (GNU) awk 将尾随 ,s 移动到后续行的开头,然后是 sed 删除。

awk -v RS= '{gsub(/,\n/, "\n,"); print}'  ~/Downloads/dump.sql | 
sed '/\s*CONSTRAINT/d' > ~/ouput.sql

这给了我以下,应该是有效的 SQL

DROP TABLE IF EXISTS `qalnk`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `qalnk` (
  `id` bigint(20) NOT NULL
,  `answer_id` bigint(20) DEFAULT NULL
,  `date_deleted` bigint(20) DEFAULT NULL
,  `deleted_by_user_ap_id` varchar(36) DEFAULT NULL
,  `expression_id` bigint(20) DEFAULT NULL
,  `expression_type` varchar(255) DEFAULT NULL
,  `ordering` int(11) DEFAULT NULL
,  `question_id` bigint(20) DEFAULT NULL
,  `expression_for_deselect_id` bigint(20) DEFAULT NULL
,  PRIMARY KEY (`id`)
,  KEY `FK6661B19F393DFCD` (`expression_id`)
,  KEY `FK6661B195182DDCD` (`question_id`)
,  KEY `FK6661B195742A56B` (`expression_for_deselect_id`)
,  KEY `idx_qlnk_nswrd` (`answer_id`)
,  KEY `FK6661B19126D878D` (`answer_id`)
,  KEY `FK6661B1975B33071` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

【讨论】:

    【解决方案3】:

    搜索任何具有“CONSTRAINT”表达式的行并删除该行之前的行和,

    sed -e '/.*/{
    N
    s/,\n.*CONSTRAINT//g}' -e '/.*CONSTRAINT.*/d' my_file
    

    【讨论】:

    • 所以他想在有约束的行之前删除逗号然后删除有约束的行?
    • 是的,但前提是约束前面的行是封闭 SQL 语句的 last 行。这是一个简单的语法要求:当你用,分隔SQL语句内部子句时,last子句不能以,结尾.
    • 事实证明,即使你修复并清理了这个解决方案(GNU sed: sed 'N; s/,\n.*CONSTRAINT.*$//; /CONSTRAINT/d' my_file),它也只能在 even 个约束条件下工作,因为与N读取行。
    • @mklement0 这就是我输入“-e '/.*CONSTRAINT.*/d'”的原因
    【解决方案4】:

    尽管投了反对票,但我认为这个答案提供了有效的解决方案,可以像宣传的那样工作,同时(希望)也能提供信息。如果不是,请告诉我们,以便我修复它。

    单通sed 解决方案要求一次读取所有输入行,类似于Ed Morton's helpful awk answer

    一个 GNU sed 解决方案

    sed -zr 's/,\n\s*CONSTRAINT\s+[^\n,]+//g' file
    
    • -z 使用 NUL(空字节)作为输入行分隔符,并且由于输入中没有嵌入的 NUL,因此将file 的全部内容一次读入模式空间。

    • -r 启用扩展正则表达式(现代语法、扩展功能)。

    • 正则表达式删除所有CONSTRAINT 行,包括前一行中的,\n,从而使封闭CREATE TABLE 语句的语法保持不变。

    不幸的是,BSD (macOS)sed 解决方案要麻烦得多:

    BSD sed 版本缺少许多 GNU 的(非标准)便利功能,这使得解决方案更加痛苦。 BSD sed 仅对 POSIX 标准提供了一些扩展,但值得注意的是能够使用所谓的扩展正则表达式。

    sed -E ':a
    $!{N;ba
    }
    s/,\n([[:blank:]]*CONSTRAINT[[:blank:]]+[[:print:][:blank:]]+\n)+/\
    /g' file
    
    • -E - 类似于 GNU sed-r - 启用扩展正则表达式。

    • :a\n$!{N;b\na} 是一个常见的sed 习惯用法,它会一次读取整个输入:

      • :a 定义要跳转到的标签。
      • $! 匹配每一行 (!) 最后一行 ($)
      • {N;ba}; 将下一行读入模式空间(要操作的输入缓冲区),然后分支 (b) 以标记 a (:a)。
      • 换句话说:这会将所有行读入模式空间,这是随后的命令操作的内容(在本例中为s)。
    • 注意所需的换行符,用于终止分支标签和分支命令,以及(以转义形式)在替换命令中。

      • 可以通过使用多个 -e 选项将其塞进一行,但这会降低命令的可读性。
    • 注意使用详细的 POSIX 字符类,例如 [[:blank:]],因为不支持快捷方式类,例如 \s
      • 特别是,虽然\n 可以在正则表达式中匹配原则上,但在字符类 中却无法识别它。因此,[^\n] 必须与[[:print:][:blank:]] 进行模拟——添加[:blank:] 以匹配制表符字符。不被视为可打印 字符; (\t 也不能在 char. 类中使用)。

    【讨论】:

    • 我得到了 sed: 非法选项 -- r
    • @GroovyEd:这意味着你使用GNU sed(在Linux上的股票sed i> 发行版);请尝试使用符合 POSIX 标准的解决方案。你在哪个平台上?
    • 我在mac上,我相信我只是使用默认sed
    • @GroovyEd: Macs (OS X) 使用 sed 的 BSD 版本,它不如 GNU 版本强大并且有很多怪癖 - 将其与 GNU sed 进行对比,请参阅 @987654322 @。一般来说,您应该在询问 awksed 问题时指明目标平台 - 除非您正在寻找真正的多平台、符合 POSIX 的解决方案(这通常会更麻烦)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-13
    • 1970-01-01
    • 2014-07-27
    • 2021-10-16
    相关资源
    最近更新 更多