每次找到匹配项时,我想打印上一行。我了解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
\n
D
\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'
...如果模式空间以字符串开头,则仅附加\n
ewline 后跟ext 输入行N
姓名,并且仅p
在模式空间不以字符串结尾时打印一行以输出10 和如果sed
可以成功地s///
替换掉后面\n
跟着字符串的 ewline年龄以及接下来的所有内容,直到模式空间的尾部。\n
因为除了作为编辑命令(例如 ext)的结果之外,模式空间中不能有 ewline,因此N
确保了唯一的姓名打印的行是紧接在年龄行不是以字符串结尾10。
上面答案中使用的所有语法都是 POSIX 标准 - 它应该与sed
支持该标准的任何语法一样工作。