How to remove only the first occurrence of a line in a file using sed

I have the following file

titi tata toto tata 

If I do

 sed -i "/tat/d" file.txt 

It will delete all lines containing tat . The command returns:

 titi toto 

but I want to remove only the first line that appears in the file containing tat :

 titi toto tata 

How can i do this?

+6
source share
6 answers

You can use a two-address form:

 sed '0,/tat/{/tat/d;}' inputfile 

This will remove the first occurrence of the pattern.

Quote from 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. 
+5
source

If you can use awk , then this does this:

 $ awk '/tata/ && !f{f=1; next} 1' file titi toto tata 

To save the result to the current file, run

 awk '...' file > tmp_file && mv tmp_file file 

Explanation

Let the flag be activated when tata maps for the first time and skips a line. From now on, do not skip these lines.

  • /tata/ matches strings containing the tata string.
  • {f=1; next} {f=1; next} sets the flag f to 1, and then skips the line.
  • !f{} , if the f flag is set, skip this block.
  • 1 , as True, performs the default awk action: {print $0} .

Another approach, Tom Fenech

 awk '!/tata/ || f++' file 

|| means OR, therefore this condition is true and therefore prints a line when any of these events occurs:

  • tata not found in string.
  • f++ is the truth. This is the hard part: the first time f is 0 by default, so first f++ will return False and will not print the line. From now on, it will increase from an integer value and will be True.
+3
source

Here is one way to do this with sed :

 sed ':a;$!{N;ba};s/\ntat[^\n]*//' file titi toto tata 
+2
source

Here is a general way to do this:

 $ 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 

Please note that using the above approach, you can skip any occurrences of RE that you like (1st, 2nd, 1st and 2nd, independently), and you will specify only one RE (unlike necessary duplicate it for some alternative solutions).

Clear, simple, obvious, easy to repair, extensible, etc.

+1
source

This may work for you (GNU sed):

 sed '/pattern/{x;//!d;x}' file 

Print all lines other than those containing the template, as usual. Otherwise, if the line contains a pattern and holds a space (first occurrence), delete this line.

+1
source

You can find the first number of the corresponding line with grep and pass it to sed for deletion.

 sed "$((grep -nm1 tat file.txt || echo 1000000000:) | cut -f 1 -d:) d" file.txt 

grep -n in combination with cut finds the line number to delete. grep -m1 provides no more than one line number. echo handles the case when there is no match so as not to return an empty result. sed "[line number] d" deletes the line.

-1
source

Source: https://habr.com/ru/post/969375/


All Articles