使用正则表达式字符串,如何删除包含匹配项的第一行之前的所有行?例如我怎样才能改变这个:
lost
load
linux
loan
linux
进入这个:
linux
loan
linux
我试过:
echo "lost
load
linux
loan
linux" | sed -e 's/.*^li.*$//g'
但它返回这个,没有改变任何东西:
lost
load
linux
loan
linux
我想让它工作,这样当没有匹配时它就不会输出任何内容。
答案1
一种方式,POSIXly:
$ echo "lost
load
linux
loan
linux" | sed -e/linux/\{ -e:1 -en\;b1 -e\} -ed
或更短:
sed -n '/linux/,$p'
甚至更短:
sed '/linux/,$!d'
对于想知道为什么我更喜欢较长版本而不是较短版本的读者,较长版本只会对文件的其余部分执行 I/O,而如果第二个地址是正则表达式,则使用范围会影响性能,并且正则表达式会尝试匹配超出必要的范围。
考虑:
$ time seq 1000000 | sed -ne '/^1$/{' -e:1 -en\;b1 -e\}
=====
JOB sed -e '/^1$/,$d'
87% cpu
0.11s real
0.10s user
0.00s sys
和:
$ time seq 1000000 | sed -e '/^1$/,/1000000/d'
=====
JOB sed -e '/^1$/,/1000000/d'
96% cpu
0.24s real
0.23s user
0.00s sys
你可以看到两个版本之间的差异。对于复杂的正则表达式,这将会有很大的不同。
答案2
这很容易清楚地做到awk
:
echo "lost
load
linux
loan
linux" | awk '
/^li/ { found = 1 }
found { print }'
这found
是一个变量,具有任意选择的、不言自明的名称。当程序遇到与正则表达式匹配的输入行时,它就会被设置。 (变量最初默认为 null,这在功能上相当于 0 或 FALSE。)因此输入行在^li
模式匹配之后打印,而不是之前。打印输入的第三行(第一linux
行),因为条件打印语句位于查找模式并设置标志的语句之后。如果您想从第四行开始打印(该行后第一linux
行),只需颠倒两个语句的顺序即可。
如果没有输入行与正则表达式匹配,则永远不会设置该标志,并且不会打印任何内容。
正如我所说,标志变量的名称是任意的;f
如果需要,您可以使用更短的内容(例如, )。 And{ print }
是默认操作,因此您可以将其省略。所以,如果你不关心清晰度,你可以将上面的内容缩短为
echo "lost
load
linux
loan
linux" | awk '/^li/{f=1}f'
答案3
另外两个 awk 解决方案:
它们都found
在看到第一个正则表达式匹配时设置一个标志,并在设置该标志时进行打印。
echo "lost
load
linux
loan
linux" | awk 'BEGIN {found = 0} {if (found || $0 ~ /linux/) {found = 1; print}}'
这个有点长,但没有found
再次设置标志。
echo "lost
load
linux
loan
linux" | awk 'BEGIN {found = 0} {if (found) {print} else if ($0 ~ /linux/) {found = 1; print}}'
答案4
您可以使用前任在批处理模式下直接编辑文件。 (如果您想在实际更改文件之前查看输出文件是什么,请将 替换为x
。%p
)
printf '%s\n' 'a' 'linux' '.' '1,/linux/-1d' '$d' 'x' | ex -s file
a
,linux
, writes在末尾.
添加一行。linux
1,/linux/-1d
删除区间 [文件的第一行,第一个之前的行linux
] 中的行;$d
删除步骤 1 中人工插入的行。x
写入更改并退出。
更直接的方法(参见编辑历史中的第一个版本) 如果没有匹配项,文件将保持不变。这会根据需要清空文件(这就是奇怪的步骤 1 的原因)。
$ cat file1
lost
load
linux
loan
linux
$ printf '%s\n' a linux . 1,/linux/-1d '$d' x | ex -s file1
$ cat file1
linux
loan
linux
$ cat file2
lost
load
loan
$ printf '%s\n' a linux . 1,/linux/-1d '$d' x | ex -s file2
$ cat file2 #file2 is empty