使用 sed 在模式匹配后打印上一行?

使用 sed 在模式匹配后打印上一行?

每次找到匹配项时,我想打印上一行。我了解grep -A-B选择。但我的 Solaris 5.10 计算机不支持这些选项。

我想要仅使用sed.

Foo.txt:

Name is : sara
age is : 10
Name is : john
age is : 20
Name is : Ron
age is : 10
Name is : peggy
age is : 30

Out.txt:

Name is : sara
Name is : Ron

我试图匹配的模式是age is : 10.

我的环境,Solaris 5.10。

答案1

$ sed -n '/age is : 10/{x;p;d;}; x' Foo.txt 
Name is : sara
Name is : Ron

以上是在 GNU sed 上测试的。如果 Solaris 的 sed 不支持将命令与分号链接在一起,请尝试:

$ sed -n -e '/age is : 10/{x;p;d;}' -e x Foo.txt 
Name is : sara
Name is : Ron

怎么运行的

sed 有一个保持空间和一个模式空间。换行符被读入模式空间。该脚本的想法是将前一行保存在保留空间中。

  • /age is : 10/{x;p;d;}

    如果当前行包含age is : 10,则执行:

    x:交换模式和保留空间,使前一行位于模式空间中

    p:打印前一行

    d:删除模式空间并开始处理下一行

  • x

    这仅在不包含 的行上执行age is : 10。在这种情况下,它将当前行保存在保留空间中。

做相反的事

假设我们要打印年龄为不是10:

$ sed -n -e '/age is : 10/{x;d}' -e '/age is :/{x;p;d;}' -e x Foo.txt 
Name is : john
Name is : peggy

上面在开头添加了一个命令 ,/age is : 10/{x;d}以忽略任何 10 岁以下的人。后面的命令/age is :/{x;p;d;}现在接受所有剩余的年龄。

答案2

POSIXly:

$ sed -n '/age is : 10/{g
1!p
}
h
' file

如果当前行不匹配,则打印上一行age is : 10

$ sed -n '
$!N
/age is : 10/d
P
' file

答案3

旧的缓冲区h适合存储一行(或一组线)直到后来的一些测试证明是正确的。换句话说,它非常适合处理您想要连续但不连续的数据序列然而顺序 - 因为它使您能够将它们粘在一起。但它也需要两个缓冲区之间进行大量复制。如果您使用旧命令构建一系列行(只是附加),这还不错,H但每次更改x缓冲区时,您都会将整个缓冲区复制到另一个缓冲区,反之亦然。

当您处理一系列已经连续的行时,并且您想根据上下文修剪它们,那么更好的方法是使用look-h- 与旧缓冲区的外观相反 -在后面cuonglm 这样做已经是他答案的后半部分了 - 但您可以将这种逻辑用于任何一种形式。

sed '$!N;/\nage.*: 10/P;D' <infile >outfile

请注意,这会将N嵌入的 ewline 定界符后面的 ext 输入行附加到非最后\n一行的当前模式空间中。然后,它检查刚刚拉出的行是否与模式匹配,如果是,则它仅打印到模式空间中的第一个 ewline - 因此仅打印前一行。最后,它删除模式空间中的第一个行并再次开始循环。因此,在整个文件中,您保持一行外观 -!$P\nD\n无需不必要地交换缓冲区。

如果我稍微改变命令,您可以具体看到它是如何工作的 - 通过在整个文件上滑动两行窗口。我将l在以下位置之前添加一个ook 命令D

sed '$!N;/\nage.*: 10/P;l;D'

Name is : sara
Name is : sara\nage is : 10$
age is : 10\nName is : john$
Name is : john\nage is : 20$
age is : 20\nName is : Ron$
Name is : Ron
Name is : Ron\nage is : 10$
age is : 10\nName is : peggy$
Name is : peggy\nage is : 30$
age is : 30$

这就是它的输出。以 结尾的行$是 ook 命令的结果l- 该命令将模式空间的转义版本呈现到标准输出。不以 结尾的行$是否则会被P打印的行。正如您所看到的,仅P当模式空间中的第二行(N刚刚拉入的 ext 行,并且位于\n模式空间中的 ewline 之后)与您的模式匹配时,才会打印前一行。

除了已经为您提供的解决方案之外,您还可以选择仅用于打印的另一种方法姓名前面的行年龄不是以 10 结尾:

sed -n '/^Name/N;/ 10$/!s/\nage.*//p'

...如果模式空间以字符串开头,则仅附加\newline 后跟ext 输入行N姓名,并且仅p在模式空间不以字符串结尾时打印一行以输出10 如果sed可以成功地s///替换掉后面\n跟着字符串的 ewline年龄以及接下来的所有内容,直到模式空间的尾部。\n因为除了作为编辑命令(例如 ext)的结果之外,模式空间中不能有 ewline,因此N确保了唯一的姓名打印的行是紧接在年龄不是以字符串结尾10

上面答案中使用的所有语法都是 POSIX 标准 - 它应该与sed支持该标准的任何语法一样工作。

相关内容