49

I need to remove odd lines in a text file to make a down-sampling. I've found this command,

awk 'NR%2==0' file

but it only prints the odd lines in the terminal. How to really remove them?

I don't really care for even or odd, I want them removed from the file or printed in another file. This only prints them in the terminal.

codeforester
  • 39,467
  • 16
  • 112
  • 140
SamuelNLP
  • 4,038
  • 9
  • 59
  • 102

6 Answers6

87

awk

The % is a modulus operator and NR is the current line number, so NR%2==0 is true only for even lines and will invoke the default rule for them ({ print $0 }). Thus to save only the even lines, redirect the output from awk to a new file:

awk 'NR%2==0' infile > outfile

sed

You can accomplish the same thing with sed. devnulls answer shows how to do it with GNU sed. Below are alternatives for versions of sed that do not have the ~ operator:

keep odd lines

sed 'n; d' infile > outfile

keep even lines

sed '1d; n; d' infile > outfile
Community
  • 1
  • 1
Thor
  • 45,082
  • 11
  • 119
  • 130
  • Ok,I don't really care for even or odd, I want them removed from the file or printed in another file. This only prints them in the terminal. – SamuelNLP Jan 23 '14 at 13:08
  • 3
    Great stuff; just to complete the picture: `awk 'NR%2!=0' infile > outfile` saves the *odd* lines; with `awk`, you canNOT replace the input file *in place*, with `sed`, you can: use option `-i ''` (on Linux, just `-i` works, too). – mklement0 Jan 23 '14 at 21:08
18

Using GNU sed:

sed -i '0~2d' filename

to remove the even numbered lines from the file.

For removing odd numbered lines:

sed -i '1~2d' filename

The -i option would cause the changes to be saved to the file in-place.

Quoting from the manual:

`FIRST~STEP'
     This GNU extension matches every STEPth line starting with line
     FIRST.  In particular, lines will be selected when there exists a
     non-negative N such that the current line-number equals FIRST + (N
     * STEP).  Thus, to select the odd-numbered lines, one would use
     `1~2'; to pick every third line starting with the second, `2~3'
     would be used; to pick every fifth line starting with the tenth,
     use `10~5'; and `50~0' is just an obscure way of saying `50'.
Thor
  • 45,082
  • 11
  • 119
  • 130
devnull
  • 118,548
  • 33
  • 236
  • 227
  • 7
    Great; to potentially save others a trip to `man sed`: address `m~n` means: "start with line m and match ever n-th line after" (and `d` means "delete"). – mklement0 Jan 23 '14 at 21:10
12

This might work for you (both GNU and non-GNU sed):

 sed -n 'p;n' file # keep odd
 sed -n 'n;p' file # keep even

-n: suppress printing

p: print current line

n: next line

wisbucky
  • 33,218
  • 10
  • 150
  • 101
potong
  • 55,640
  • 6
  • 51
  • 83
5

Don't focus on the negative (removing lines), focus on the positive (selecting lines) and your solution will follow suit. So instead of I need to remove odd lines you should be thinking I need to select even lines and then the solution is simply:

awk '!(NR%2)' file

If you want to save the result to a new file:

awk '!(NR%2)' file > newfile

or back to the original:

awk '!(NR%2)' file > newfile && mv newfile file
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
3

Here's an awk example to create two new files containing the odd and even lines, respectively:

awk '{ if (NR%2) print > "odd.txt"; else print > "even.txt" }' input.txt
twalberg
  • 59,951
  • 11
  • 89
  • 84
  • 1
    ITYM to use `>`, not `>>` (remember this is awk, not shell). Also, you can abbreviate the whole thing to `{ print > ((NR%2?"odd":"even") ".txt") }` – Ed Morton Jan 23 '14 at 18:54
  • Valid point, although if neither of the files existed beforehand, they're fairly equivalent. And the abbreviated code, at least to me, is a little less readable, so, while I might do it for scripts I maintain, I don't always recommend that level of terseness for others... – twalberg Jan 23 '14 at 18:59
1

Perl solution for printing evens to new file:

perl -lne 'print if $. % 2 == 0' infile > outfile

To print odds, change == 1 to == 0

$. is the line number

Keeps only evens in the original file:

perl -i -lne 'print if $. % 2 == 0' infile

Same as above, but makes a backup file called infile.bak:

perl -i.bak -lne 'print if $. % 2 == 0' infile
Chris Koknat
  • 3,305
  • 2
  • 29
  • 30