Text-Processing
刪除多行字元串
這裡有幾個關於使用 Unix shell 替換多行字元串的問題,但我還沒有找到一個可以在這種情況下工作的問題。
我正在嘗試從一些 MySQL DDL 中刪除鍵和約束,如下所示(一個範例):
CREATE TABLE `access_group` ( `GROUP_ID` int(10) NOT NULL AUTO_INCREMENT, `PARENT_GROUP_ID` int(10) DEFAULT NULL, `GROUP_NAME` varchar(45) NOT NULL, `GROUP_DESC` varchar(45) NOT NULL DEFAULT '', PRIMARY KEY (`GROUP_ID`), KEY `testkey` (`PARENT_GROUP_ID`) ) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=latin1;
我想刪除從逗號結束’PRIMARY KEY’之前的行到但不包括’)ENGINE =‘的所有內容(這些之間可以有零行或多行,它們不會總是以KEY開頭或有括號,但 ‘) ENGINE=’ 是一致的)。結果應如下所示:
CREATE TABLE `access_group` ( `GROUP_ID` int(10) NOT NULL AUTO_INCREMENT, `PARENT_GROUP_ID` int(10) DEFAULT NULL, `GROUP_NAME` varchar(45) NOT NULL, `GROUP_DESC` varchar(45) NOT NULL DEFAULT '' ) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=latin1;
我願意使用任何標準的命令行實用程序(例如 sed、perl、awk),但由於這些文件可能相當大(有些文件大約為數十或數百 GB),因此它們需要高效。由於文件通常以 gzip 格式儲存(或者有時我直接處理 mysql 轉儲實用程序的輸出,而不是首先寫入磁碟),因此我需要可以通過管道輸入和輸出的東西。
保持狀態是否列印上一行,編輯表示在必要時刪除逗號。這種方法只在記憶體中保留一兩行文件。
#!/usr/bin/env perl use strict; use warnings; my $printing = 1; my $previous; # reads from standard input (optionally with the conventional -) or from # the named files shift @ARGV if @ARGV == 1 and $ARGV[0] eq '-'; while ( my $line = readline ) { if ( $line =~ m/^\s+PRIMARY KEY/ ) { $previous =~ s/,[ \t]*$//; $printing = 0; } elsif ( $line =~ m/^\) ENGINE/ ) { $printing = 1; } elsif ( !$printing ) { undef $previous; } print $previous if defined $previous; $previous = $line if $printing; } # don't forget last line after fall off the end of input (eof) print $previous if defined $previous;
使用
ex
(又名vim
Ex 模式):ex +'%s/,\n *PRIM\_.*\ze\n) ENGINE//' +wq file
只是 Vim 替換刪除(空替換
//
)的“批處理”版本,它與 進行多行匹配\_.*
並排除模式的最後部分 with\ze
。這會就地修改文件。如果您不希望這樣做以保存到新文件
file2
:ex +'%s/,\n *PRIM\_.*\ze\n) ENGINE//' +'w file2' +q! file
**更新:**在文件中管道…這有點不尋常,
/dev/stdin
但可以解決問題:cat file | ex +'%s/,\n *PRIM\_.*\ze\n) ENGINE//' +'%p|q!' /dev/stdin