bash 如何使用sed仅删除文件中第一次出现的行
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23696871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to remove only the first occurrence of a line in a file using sed
提问by MOHAMED
I have the following file
我有以下文件
titi
tata
toto
tata
If I execute
如果我执行
sed -i "/tat/d" file.txt
It will remove all the lines containing tat
. The command returns:
它将删除所有包含tat
. 命令返回:
titi
toto
but I want to remove only the first line that occurs in the file containing tat
:
但我只想删除包含tat
以下内容的文件中出现的第一行:
titi
toto
tata
How can I do that?
我怎样才能做到这一点?
回答by devnull
You could make use of two-address form:
您可以使用两个地址形式:
sed '0,/tat/{/tat/d;}' inputfile
This would delete the first occurrence of the pattern.
这将删除第一次出现的模式。
Quoting from info sed
:
引自info sed
:
A line number of `0' can be used in an address specification like
`0,/REGEXP/' so that `sed' will try to match REGEXP in the first
input line too. In other words, `0,/REGEXP/' is similar to
`1,/REGEXP/', except that if ADDR2 matches the very first line of
input the `0,/REGEXP/' form will consider it to end the range,
whereas the `1,/REGEXP/' form will match the beginning of its
range and hence make the range span up to the _second_ occurrence
of the regular expression.
回答by fedorqui 'SO stop harming'
If you can use awk
, then this makes it:
如果你可以使用awk
,那么这使得它:
$ awk '/tata/ && !f{f=1; next} 1' file
titi
toto
tata
To save your result in the current file, do
要将结果保存在当前文件中,请执行
awk '...' file > tmp_file && mv tmp_file file
Explanation
解释
Let's activate a flag whenever tata
is matched for the first time and skip the line. From that moment, keep not-skipping these lines.
让我们tata
在第一次匹配时激活一个标志并跳过该行。从那一刻起,请继续不要跳过这些行。
/tata/
matches lines that contain the stringtata
.{f=1; next}
sets flagf
as 1 and then skips the line.!f{}
if the flagf
is set, skip this block.1
, as a True value, performs the default awk action:{print $0}
.
/tata/
匹配包含字符串的行tata
。{f=1; next}
将标志设置f
为 1,然后跳过该行。!f{}
如果f
设置了标志,则跳过此块。1
,作为一个真正的价值,执行默认AWK行动:{print $0}
。
Another approach, by Tom Fenech
另一种方法,作者:Tom Fenech
awk '!/tata/ || f++' file
||
stands for OR, so this condition is true, and hence prints the line, whenever any of these happens:
||
代表 OR,因此此条件为真,因此只要发生以下任一情况,就会打印该行:
tata
is not found in the line.f++
is true. This is the tricky part: first time f is 0 as default, so firstf++
will return False and not print the line. From that moment, it will increment from an integer value and will be True.
tata
未在该行中找到。f++
是真的。这是棘手的部分:第一次 f 默认为 0,所以 firstf++
将返回 False 并且不打印该行。从那一刻起,它将从一个整数值递增并且为 True。
回答by jaypal singh
Here is one way of doing it with sed
:
这是一种方法sed
:
sed ':a;$!{N;ba};s/\ntat[^\n]*//' file
titi
toto
tata
回答by Ed Morton
Here's the general way to do it:
这是执行此操作的一般方法:
$ cat file
1 titi
2 tata
3 toto
4 tata
5 foo
6 tata
7 bar
$
$ awk '/tat/{ if (++f == 1) next} 1' file
1 titi
3 toto
4 tata
5 foo
6 tata
7 bar
$
$ awk '/tat/{ if (++f == 2) next} 1' file
1 titi
2 tata
3 toto
5 foo
6 tata
7 bar
$
$ awk '/tat/{ if (++f ~ /^(1|2)$/) next} 1' file
1 titi
3 toto
5 foo
6 tata
7 bar
Note that with the above approach you can skip whatever occurrence(s) of an RE you like (1st, 2nd, 1st and 2nd, whatever) and you only specify the RE once (as opposed to having to duplicate it for some alternative solutions).
请注意,使用上述方法,您可以跳过任何您喜欢的 RE(第 1、第 2、第 1 和第 2 次,等等)并且您只指定 RE 一次(而不是必须为某些替代解决方案复制它) .
Clear, simple, obvious, easily maintainable, extensible, etc....
清晰、简单、明显、易于维护、可扩展等......
回答by potong
This might work for you (GNU sed):
这可能对你有用(GNU sed):
sed '/pattern/{x;//!d;x}' file
Print all lines other than those containing the pattern as normal. Otherwise if the line contains the pattern and hold space does not (the first occurrence), delete that line.
正常打印除包含模式的行以外的所有行。否则,如果该行包含该模式并且保留空间不包含(第一次出现),则删除该行。
回答by Vytenis Bivainis
You may find the first matching line number with grep
and pass it to sed
for deletion.
您可以找到第一个匹配的行号grep
并将其传递给sed
删除。
sed "$((grep -nm1 tat file.txt || echo 1000000000:) | cut -f 1 -d:) d" file.txt
grep -n
combined with cut
finds the line number to be deleted. grep -m1
ensures at most one line number is found. echo
handles the case when there is no match so as not to return an empty result. sed "[line number] d"
deletes the line.
grep -n
结合cut
查找要删除的行号。grep -m1
确保最多找到一个行号。echo
处理不匹配的情况,以免返回空结果。sed "[line number] d"
删除该行。