34

I want to print the odd-numbered and even-numbered lines from files.

I found this shell script which makes use of echo.

#!/bin/bash
# Write a shell script that, given a file name as the argument will write
# the even numbered line to a file with name evenfile and odd numbered lines
# in a text file called oddfile.
# -------------------------------------------------------------------------
# Copyright (c) 2001 nixCraft project <http://cyberciti.biz/fb/>
# This script is licensed under GNU GPL version 2.0 or above
# -------------------------------------------------------------------------
# This script is part of nixCraft shell script collection (NSSC)
# Visit http://bash.cyberciti.biz/ for more information.
# -------------------------------------------------------------------------

file=$1
counter=0

eout="evenfile.$$" # even file name
oout="oddfile.$$" # odd file name

if [ $# -eq 0 ]
then
    echo "$(basename $0) file"
    exit 1
fi

if [ ! -f $file ]
then
    echo "$file not a file"
    exit 2
fi

while read line
do
    # find out odd or even line number
    isEvenNo=$( expr $counter % 2 )

    if [ $isEvenNo -ne 0 ]
    then
        # even match
        echo $line >> $eout
    else
        # odd match
        echo $line >> $oout
    fi
    # increase counter by 1
    (( counter ++ ))
done < $file
echo "Even file - $eout"
echo "Odd file - $oout"

But isn't there a way to do it in one line?

Yes, use awk, I read.

Even-numbered lines:

awk 'NR % 2' filename

odd-numbered lines:

awk 'NR % 2 == 1' filename

But it doesn't work for me. Both produce the same output, according to diff. When compared to the original file, they are both indeed half as long, and they both contain the odd-numbered lines. Am I doing something wrong?

2
  • 7
    The first one should be NR % 2 == 0, otherwise it is equivalent to the second one.
    – enzotib
    Dec 13, 2011 at 11:49
  • There seem to be several documents online (including this one) that show up at the top of a search that state that NR % 2 gives you the even numbered lines, which is not correct, it gives you the odd because 1 % 2 = 1 = true, 2 % 2 = 0 = false.
    – deltaray
    Jan 24, 2012 at 16:29

8 Answers 8

48

I prefer to be POSIX compatible, whenever possible, so I thought I'd post this alternative method. I often use these to mangle text, before xargs pipelines.

Print Even Numbered Lines,

sed -n 'n;p'

Print Odd Numbered Lines,

sed -n 'p;n'

Although I often use awk, it's overkill for this type of task.

2
19

For even numbers the code should be

awk 'NR%2==0' filename

& for odd numbers

awk 'NR%2==1' filename
2
  • 1
    this one is perfect. Even works if you need to get rows in increment of 10, say you need to reduce a ordered file of size 1million to 100k. This is exactly what I wanted.
    – Dexter
    May 6, 2017 at 23:16
  • How can you print the even-numbered columns in AWK? I cannot get this working gawk 'FS=",";NF%2==0' file.csv.
    – hhh
    Jul 20, 2017 at 20:36
16

That's easy:

 sed -n 2~2p filename

will print even-numbered lines from filename

sed -n 1~2p filename

will print odd-numbered lines.

6
  • 1
    +1, for not using AWK extraneously. Not POSIX sed, but it's still solid method. Nov 1, 2012 at 14:33
  • @TechZilla I don't understand "using AWK extraneously" - awk is POSIX as well.
    – jw013
    Nov 1, 2012 at 15:31
  • 3
    @jw013: Nothing is wrong with awk, personally I use it very often. I never said anything was 'Not POSIX' about awk, I was referring to the answer's sed options. Specifically the ~ operator, it's a GNU extension, which is still acceptable for many people. Regarding 'using AWK extraneously, I personally believe using awk` for this simple task is overkill. So the +1 was for for completing the task with sed, a lighter utility than awk. Nov 1, 2012 at 15:55
  • 2
    Can someone please explain how ~ operator works here? Apr 10, 2018 at 15:51
  • 1
    Note, this does not work on mac becuase of sed version. Use sed -n 'n;p' on mac system. Oct 13, 2020 at 9:20
13

As you asked “in one line”:

awk '{print>sprintf("%sfile.%d",NR%2?"odd":"even",PROCINFO["pid"])}' filename

Note that most of the code is due to your fancy output filename choice. Otherwise the following code would be enough to put odd lines in “line-1” and even lines in “line-0”:

awk '{print>"line-"NR%2}' filename
4

You can do it with a single sed invocation, no need to read the file twice:

sed '$!n
w even
d' infile > odd

or, if you prefer in one line:

sed -e '$!n' -e 'w even' -e d infile > odd

Note that these won't give the expected result if a file contains only one line (the line will be written to even instead of odd as the first n isn't executed). To avoid that, add a condition:

sed -e '$!n' -e '1!{w even' -e 'd}' infile > odd

How it works ? Well, it uses three sed commands:
n - if not on the last line print the pattern space to stdout(which is redirected to file odd), replace it with the next line (so now it's processing an even line) and continue executing the remaining commands
w - append the pattern space to file even
d - delete current pattern space and restart the cycle - the side effect of this is that sed will never auto-print the pattern space as it never reaches the end of script

In other words, n is executed only on odd lines and w and d are executed only on even lines. sed never gets to autoprint unless, as I said, the input consists of a single line.

1
  • Thanks a lot don_crissti for your help. Sincerely appreciate it, upvoted as well. Apr 11, 2018 at 12:08
0

Try this:

awk '{if(NR%2){print $0 > "odd.file"}else{print $0 > "even.file"}}' filename
2
  • Are you sure about outputting the record numbers?
    – manatwork
    Dec 13, 2011 at 12:17
  • sorry about that, I modified it to output the whole lines.
    – renma
    Dec 13, 2011 at 12:29
0

I'd go with perl because I like perl:

perl -pe 'BEGIN{open($e,">even_lines");open($o,">odd_lines")} $. % 2 ?select $o:select $e;'

Uses the fact that -p implicitly prints, to replicated how sed works - and we use select to choose which file handle to which it writes.

1
  • One alternate, that will do either odd or even (not both) would be to change the command line to perl -ne 'print if $. % 2 == 1' for odd and perl -ne 'print if $. % 2 == 0' for even. That doesn't do both simultaneously, but for smaller files, it's quick enough to run twice.
    – steveb
    Jul 6, 2020 at 20:48
0

Print even-numbered lines:

awk 'getline' file

Print odd-numbered lines:

awk '{print; getline}' file

You must log in to answer this question.

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