簡體   English   中英

如何 grep 帶有換行符或制表符或空格的多行字符串

[英]How to grep multi line string with new line characters or tab characters or spaces

我的測試文件包含如下文本:

> cat test.txt
new dummy("test1", random1).foo("bar1");
new dummy("
        test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
            "test4", random4).foo("bar4");

我正在嘗試匹配所有以分號 (;) 結尾並包含文本“dummy(”) 的單行。然后我需要提取 dummy 內雙引號中存在的字符串。我想出了以下命令,但它只匹配第一個和第三個聲明。

> perl -ne 'print if /dummy/ .. /;/' test.txt | grep -oP 'dummy\((.|\n)*,'
dummy("test1",
dummy("test3",

使用 -o 標志,我希望在 dummy 中的雙引號之間提取字符串。 但這也行不通。 你能給我一個關於如何進行的想法嗎?

預計 output 是:

test1
test2
test3
test4

以下一些答案適用於基本文件結構。 如果 lines 包含超過 1 個換行符,則代碼中斷。 例如輸入換行符較多的文本文件:

new dummy("test1", random1).foo("bar1");
new dummy("
        test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
            "test4", random4).foo("bar4");
new dummy("test5",
        random5).foo("bar5");
new dummy("test6", random6).foo(
        "bar6");
new dummy("test7", random7).foo("
        bar7");

我提到了以下 SO 鏈接:

如何為 grep 中的新行提供模式?

如何 grep 多行直到; (分號)

@TLP 非常接近:

perl -0777 -nE 'say for map {s/^\s+|\s+$//gr} /\bdummy\(\s*"(.+?)"/gs' test.txt
test1
test2

使用

  • -0777將文件作為單個字符串插入
  • /\bdummy\(\s*"(.+?)"/gs在 "dummy(" 之后找到所有引用的字符串內容(在開始引號之前有可選的空格)
    • s標志允許. 匹配換行符。
    • 任何包含轉義雙引號的字符串都會破壞此正則表達式
  • map {s/^\s+|\s+$//gr}從每個字符串中刪除前導/尾隨空格。

這個perl應該工作:

perl -0777 -pe 's/(?m)^[^(]* dummy\(\s*"\s*([^"]+).*/$1/g' file

test1
test2
test3
test4

以下gnu-grep + tr也應該有效:

grep -zoP '[^(]* dummy\(\s*"\s*\K[^"]+"' file | tr '"' '\n'

test1
test2
test3
test4

使用您顯示的示例,請嘗試使用 GNU awk編寫和測試的awk代碼。

awk -v RS='(^|\n)new[^;]*;' '
RT{
  rt=RT
  gsub(/\n+|[[:space:]]+/,"",rt)
  match(rt,/"[^"]*"/)
  print substr(rt,RSTART+1,RLENGTH-2)
}
'  Input_file

鑒於:

$ cat file
new dummy("test1", random1).foo("bar1");
new dummy("
        test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
            "test4", random4).foo("bar4");

你可以這樣使用 GNU grep:

$ grep -ozP '[^;]*\bdummy[^";]*"\s*\K[^";]*[^;]*;' file | tr '\000' '\n' | grep -oP '^[^"]*'
test1
test2
test3
test4

如果這是一個更健壯的; 分隔文本,您可以:

  1. ;上拆分 ;
  2. 過濾/\bdummy\b/
  3. 抓住引號中的第一個字段;
  4. 去除空白。

這是ruby中的所有內容:

ruby -e 'puts $<.read.split(/(?<=;)/).
                select{|b| b[/\bdummy\b/]}.
                map{|s| s[/(?<=")[^"]*/].strip}' file 
# same output

您可以使用Text::ParseWords來提取引用的字段。

use strict;
use warnings;
use Data::Dumper;
use Text::ParseWords;

my $str = do {
    local $/;
    <DATA>;
};   # slurp the text into a variable
my @lines = quotewords(q("), 1, $str);   # extract fields
my @txt;

for (0 .. $#lines) {
    if ($lines[$_] =~ /\bdummy\s*\(/) {
        push @txt, $lines[$_+1];         # target text will be in fields following "dummy("
    }
}

s/^\s+|\s+$//g for @txt;     # trim leading/trailing whitespace
print Dumper \@txt;

__DATA__
new dummy("test1", random1).foo("bar1");
new dummy("
        test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
            "test4", random4).foo("bar4");

Output:

$VAR1 = [
          'test1',
          'test2',
          'test3',
          'test4'
        ];

基於awk的解決方案通過FS處理一切:

<test1.txt gawk -b -e 'BEGIN { RS="^$"

 FS="((^|\\n)?"(___="[^\\n")"]+y[(]"(_="[ \\t\\n]*")(__="[\\42]")(_)\
    "|"(_="[ \\t]*")(__)(_)"[,]"(___)";]+[;][\\n])+"} sub(OFS=ORS,"",$!--NF)'          

test1
test2
test3
test4

gawk5.15 secs時以2 million rows行為基准,因此除非您的輸入文件超過100 MB ,否則這就足夠了。

*** 警告:避免在此解決方案中使用mawk-1.9.9.6

建議簡單的gawk腳本(標准 linux awk ):

 awk '/dummy/{print gensub("[[:space:]]*","",1,$2)}' RS=';' FS='"'  input.txt

解釋:

RS=';' awk記錄分隔符設置為;

FS='"'awk字段分隔符設置為"

/dummy/僅過濾與dummy RexExp 匹配的記錄

gensub("[[:space:]]*","",1,$2)從第二個字段的開頭修剪任何空白

print gensub("[[:space:]]*","",1,$2)打印修剪后的第二個字段

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM