使用 awk

使用 awk

我下面有一个示例文本文件(test_long_sentence.txt),我想 grep 包含 test1 的所有行,排除不需要的数据。

如何在报价结束前 grep 数据?

test_long_sentence.txt

This is some unwanted data blah blah blah

20  /test1/catergory="Food"
20  /test1/target="Adults, \"Goblins\", Elderly,
Babies, \"Witch\",
Faries"
20  /test1/type="Western"

This is some unwanted data blah blah blah

20  /test1/theme="Halloween"

命令:

grep "test1" test_long_sentence.txt

实际输出:

20  /test1/catergory="food"
20  /test1/target="Adults, \"Goblins\", Elderly,
20  /test1/type="Western"
20  /test1/theme="Halloween"

预期输出:

20  /test1/catergory="food"
20  /test1/target="Adults, \"Goblins\", Elderly,
Babies, \"Witch\",
Faries"
20  /test1/type="Western"
20  /test1/theme="Halloween"

PS:我无法控制编辑test_long_sentence.txt。所以请不要要求我将其编辑为一行。

答案1

使用 awk

$ awk '/test1/{line=$0; while (!(line ~ /[^\\]".*[^\\]"/)) {getline; line=line "\n" $0}; print line}' sentence.txt 
20  /test1/catergory="Food"
20  /test1/target="Adults, \"Goblins\", Elderly,
Babies, \"Witch\",
Faries"
20  /test1/type="Western"
20  /test1/theme="Halloween"

/test1/是一个条件。如果当前行包含与 regex 匹配的内容test1,则执行花括号中的命令。这些命令是:

  • line=$0

    当前行的内容保存在变量“line”中。

  • while (!(line ~ !/[^\\]".*[^\\]"/)) {getline; line=line "\n" $0}

    如果当前内容line不包含两个未转义的引号,则获取下一行,getline并将其附加到linevialine=line "\n" $0

  • print line

    现在该变量line包含两个未转义的引号,我们将其打印出来。

对于那些喜欢将命令分散在多行中的人,可以将与上面相同的命令编写为:

awk '
    /test1/{
        line=$0
        while (!(line ~ /[^\\]".*[^\\]"/)) {
            getline
            line=line "\n" $0
        }
        print line
    }' sentence.txt 

使用 sed

$ sed -n '/test1/{:a; /[^\\]".*[^\\]"/{p;b}; N; ba}' sentence.txt 
20  /test1/catergory="Food"
20  /test1/target="Adults, \"Goblins\", Elderly,
Babies, \"Witch\",
Faries"
20  /test1/type="Western"
20  /test1/theme="Halloween"

怎么运行的:

  • -n

    这告诉 sed 不要打印任何内容,除非我们明确要求它打印。

  • /test1/{...}

    对于任何包含 的行test1,我们执行大括号中的命令,它们是:

    • :a

      这定义了一个标签a

    • /[^\\]".*[^\\]"/{p;b}

      如果模式空间当前包含两个未转义的引号,我们将打印模式空间 ,p然后跳过其余指令并分支 ,b以从下一行开始。

    • N

      如果我们到达这里,这意味着当前没有两个未转义的引号。我们将下一行读入模式空间。

    • ba

      我们回到标签a并重复该标签后面的命令。

答案2

这将适用于awk而不是grep针对该特定文件:

awk 'NR==3,NR==7;NR==11' test_long_sentence.txt

为了将来解决此问题,您可以运行cat -n该文件以查看要包含和排除的行。

答案3

这是一个简单的 Perl 脚本,用于连接输入中的连续行。它假设:

  • 连续的行将与单个空格字符连接。

  • “行”从任何以数字和空格开头的输入行开始,一直持续到:

    • 一个空白的像
    • 另一行以数字和空格开头
  • 空行以及空行之间的所有行都将被忽略(即丢弃)。

这可能与您的实际输入文件不完美匹配,但确实与您的问题中提供的示例输入匹配。根据需要修改代码以适合您的输入。

#!/usr/bin/perl

my $skip=1;  # start with skip = true.
my $line='';

while(<>) {
  chomp;

  if (m/^\d+\s+/) { # / this comment is only here to fix SE\'s syntax highlighting
    $skip=0;
    print $line,"\n" if ($line);
    $line = $_;

 } elsif (m/^\s*$/) {
    if ($line ne '') { print $line, "\n"; $line = ''};
    $skip = 1 - $skip;

  } elsif (! $skip) {
    $line .= " $_";
 };
};

另存为,例如,./join-lines.pl使可执行文件chmod +x ./join-lines.pl,并运行如下:

$ ./join-lines.pl test_long_sentence.txt 
20  /test1/catergory="Food"
20  /test1/target="Adults, \"Goblins\", Elderly, Babies, \"Witch\", Faries"
20  /test1/type="Western"
20  /test1/theme="Halloween"

然后可以根据需要将其输入grep或其他工具中。

请注意,对于当前样本输入, 的输出./join-lines.pl test_long_sentence.txt | grep test1(除了 可能的匹配着色之外grep)与上面的输出相同,因为所有样本行都包含“test1”。您可以更有用地 grep 查找其他匹配项,例如:

$ ./join-lines.pl test_long_sentence.txt | grep Witch
20  /test1/target="Adults, \"Goblins\", Elderly, Babies, \"Witch\", Faries"

如果您需要能够重现完全相同的输入(但没有空白和不需要的行),那么不要用空格连接行,而是使用" ===NL=== "您绝对需要的字符(例如 TAB)或更长的字符串(例如 )某些永远不会出现在输入中。例如

  } elsif (! $skip) {
    $line .= "\t$_";
 };

或者

  } elsif (! $skip) {
    $line .= " ===NL=== $_ ";
 };

然后可以将连接字符串转换回换行符,例如sed(使用制表符作为连接字符):

$ ./join-lines.pl test_long_sentence.txt  | grep Witch | sed -e 's/\t/\n/g'
20  /test1/target="Adults, \"Goblins\", Elderly,
Babies, \"Witch\",
Faries"

另一个例子,使用更长的字符串来连接行:

./join-lines.pl test_long_sentence.txt  | grep Witch | sed -e 's/ ===NL=== /\n/g'

相关内容