30

File:

Data inserted into table. Total count 13
No error occurred
Data inserted into table. Total count 45
No error occurred
Data inserted into table. Total count 14
No error occurred
Data inserted into table. Total count 90
No error occurred

Expected output file:

Data inserted into table. Total count 13
Data inserted into table. Total count 45
Data inserted into table. Total count 14
Data inserted into table. Total count 90

I want the output to look this way: every second line will be deleted but there will be no gap between lines.

3
  • 5
    Do you want to delete every second line or all lines that contains "No error occurred"? What if two consecutive lines had "No error occurred"? Aug 4, 2015 at 16:42
  • 1
    @user1598390 I think... in that case grep -v "No error occurred" file this command should work... what @paul has answered. In the output file, there will be no lines containing "No error occurred" this part.
    – Nainita
    Aug 6, 2015 at 16:38
  • 1
    Then the title of the question is misleading. Aug 6, 2015 at 20:17

10 Answers 10

63

Solving this by deleting every second line can be error prone (for example, when process sometimes generates two meaningful lines instead one). May be it is better to filter out the garbage:

grep -v "No error occurred" file

It can run as filter, you can add more garbage patterns here and improve the result.

1
  • 9
    +1 for pointing out that sometimes the second line is important!
    – Kaz Wolfe
    Aug 4, 2015 at 3:45
44
+50

With sed:

sed -e n\;d <file

With POSIX awk:

awk 'FNR%2' <file

If you have older awk (like oawk), you need:

oawk 'NR%2 == 1' <file

With ex:

$ ex file <<\EX
:g/$/+d
:wq!
EX

will edit the file in-place.

  • g mark a global command
  • /$/ match every lines
  • +d delete the next line
  • wq! save all changes

This approach share the same ideal with sed approach, delete every next line of current line start from line 1.

With perl:

perl -ne 'print if $. % 2' <file

and raku:

raku -ne '.say if $*IN.ins % 2' <file
raku -ne '.say if ++$ % 2' <file

Edit

Raku IO::Handle.ins was removed in this commit.

17
  • Yes... its working ... :) ... first one is working.... i have also tried the second one.. its telling `awk : syntax error line1 awk: bailing out near line 1'
    – Nainita
    Aug 3, 2015 at 10:03
  • 1
    I'm guessing that you used n\;d instead of 'n;d' to save a precious character but that logic goes out of the window when you're unnecessarily using the -e switch and a file redirection <!
    – Tom Fenech
    Aug 4, 2015 at 11:50
  • 1
    @Geek: It's just a shorter version of sed -e 'n;d', save you one character.
    – cuonglm
    Sep 30, 2015 at 6:21
  • 1
    @Geek: n command write pattern space to standard output if -n was used, then replace pattern space with next line. Here's every odd line will be printed by n, even line then read into pattern space but delete immediately by d command`.
    – cuonglm
    Sep 30, 2015 at 6:37
  • 1
    @jubilatious1 .ins was removed in commit github.com/rakudo/rakudo/commit/…
    – cuonglm
    Sep 7, 2020 at 3:56
13

Ассоrding to the question, with GNU sed:

sed '0~2d' file

will delete every second line but I'd like to offer filter lines by it content:

sed '/Data/! d' file

or with same result

sed '/No error/d' file
2
  • sed '/No error/d' file ~ gives the desired output @Costas
    – Nainita
    Aug 3, 2015 at 10:08
  • 5
    Note that the last two are convoluted ways to write grep Data and grep -v 'No error' Aug 4, 2015 at 9:14
5

Here is a way using sed:

sed -n 'p;n' filename

Another way with GNU sed:

sed -n '1~2p' filename

Output from above commands:

Data inserted into table. Total count 13
Data inserted into table. Total count 45
Data inserted into table. Total count 14
Data inserted into table. Total count 90
4
  • What do you mean when saying shortest way using sed?
    – cuonglm
    Aug 3, 2015 at 12:08
  • What is the reason in g command? sed -n 'p;n' is enough.
    – Costas
    Aug 3, 2015 at 12:14
  • @cuonglm: I mean to say simple way of doing. By the way removed that word. :)
    – serenesat
    Aug 3, 2015 at 12:22
  • @Costas: Thanks! Just checked, its working without g. removed g from command. :)
    – serenesat
    Aug 3, 2015 at 12:24
5

Another answer, you could use vi/vim!

qdjddq

And then if your file was 500 lines (for example) type

250@d

And then to write and exit type

:x

Or if something goes wrong and you don't want to save:

:q!

Explanation:

q      #Start Recording
 d     #Put the recording into register 'd'
  j    #Move the cursor down
   dd  #Delete the line
     q #Stop recording


250    #Number of repeats
   @d  #Playback the recording in register 'd'.
4

You can try with awk:

awk 'NR % 2 != 0' file

or you can print only lines containing Data inserted:

awk '$0 ~ /Data inserted/' file
1
  • I have tried both of you answers and both are working... :)
    – Nainita
    Aug 3, 2015 at 10:05
2

Here's quite a different way of doing it:

< file paste - - | cut -f1

This assumes that the odd-numbered lines do not contain tabs. If they do, then you'll need to pick another separator character, e.g. : here:

< file paste -d: - - | cut -d: -f1
3
  • 1
    I had this in mind when I first saw the question... It'd be interesting to run a speed test against sed with a huge file (e.g. 20 mil lines). Anyways, +1 but really, to avoid headaches, pick a delimiter that's unlikely to occur in a text file, like $'\002'... Aug 7, 2015 at 0:29
  • @don_crissti yes use of a non-printing character for the separator is a good idea. And yes, this is measurably faster than the sed solution. I created a test file with seq 100000000 > 100mil.txt. The paste|cut solution finished in about 7.5 seconds, vs nearly 12 for the sed solution. Seems to be repeatable. grep is fastest though. Ubuntu 14.04 with standard GNU tools. Aug 7, 2015 at 0:40
  • Yep, paste + cut are heavily optimized for their job so unsurprisingly their combination is pretty damn fast... Aug 7, 2015 at 0:45
1

Another option (shorter)

sed 'n; d' file
1
  • 3
    It's longer than mine sed n\;d, adding -e is only my habit.
    – cuonglm
    Aug 3, 2015 at 13:05
0

It also solves the problem, though it is a bit slower:

vim -c "%normal jdd" -c "wq" file
0

Using Raku (formerly known as Perl_6)

raku -pe 'get;' file

Sample Input:

Data inserted into table. Total count 13
No error occurred
Data inserted into table. Total count 45
No error occurred
Data inserted into table. Total count 14
No error occurred
Data inserted into table. Total count 90
No error occurred

Sample Output:

Data inserted into table. Total count 13
Data inserted into table. Total count 45
Data inserted into table. Total count 14
Data inserted into table. Total count 90

There's already an answer using Raku from @cuonglm, which uses the one-liner raku -ne '.say if ++$ % 2'. Above is a different approach using Raku's get routine instead. In combination with the -pe (autoprinting) command-line flags, Raku's get routine advances the readline-cursor by a line, which (because -pe prints return values not the $_ topic variable) results in that line get-ting discarded. Only the first, third, fifth, etc. lines are printed.

Need to print the second, fourth, sixth lines, etc. instead of above? Just use Raku's -ne command-line flags. Here Raku's get routine advances the readline-cursor by a line, and then .put is called, giving the following output:

~$ raku -ne 'get.put;'  file
No error occurred
No error occurred
No error occurred
No error occurred

https://raku.org

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .