1

Say I have a file:

Line1
Line2
Line3
Line4
Line5 
Line6
Line7 
Line8 
Line9
Line10
Line11
Line12
…

I want to get:

Line1
Line2
Line3
Line7 
Line8 
Line9
Line13
Line14
Line15

It means that I want the odd number of three lines (including).

I have some ideas like for (i=1, i<=FR, i+5), {sed -n,'i, i+1p' } < input

Could you tell me the structure of for loop in sed. And how to correct this?

1
  • 3
    So you want lines where lineNumber mod 6 is equal to 1,2 or 3. Jul 8, 2015 at 21:56

2 Answers 2

3

Try this

awk 'NR % 6 == 1 || NR % 6 == 2 || NR % 6 == 3' filename.txt > output.txt
3
  • 1
    awk '{n = NR % 6} 1 <= n && n <= 3' is more concise Jul 8, 2015 at 22:02
  • 4
    Still more concise: awk '(NR-1) % 6 < 3'
    – John1024
    Jul 8, 2015 at 22:04
  • 3
    Yet another approach: awk 'NR%6%5%4'
    – jimmij
    Jul 8, 2015 at 22:15
1
seq 20|
sed 'n;n;n;$!N;$!N;d'

n overwrites the current line with the next input line and prints the pattern space it overwrites (unless you do sed -n or sed -e'#n'), so we do that three times, then we buffer two more appended input lines, and delete the lot of them.

1
2
3
7
8
9
13
14
15
19
20

The other thing I had was like this:

sed -ne:p -e'$p;N;s/\n/&/2p;tn' -ebp \
     -e:n -en \
     -e:d -e'N;s/\n/&/2;t'      -ebd

Which will first buffer two appended Next input lines and count the \newlines added and print if the current line is the last or if the buffer is full, then overwrite that w/ the next input line, and buffer two more before deleting those as well.

It might also be written:

sed -ne:p -e'$p;N;s/\n/&/2p;tn' \
     -ebp -e:n -e'n;n;n'

But the advantage to the tests is that you can easily parameterize it like:

altl(){ sed -ne":t$(n=0 nt='\n\t' b=$nt\b
        while   [ "$#" -gt "$((!(n+=1)))" ]  &&
                c=${1%?}        p=${1##*[!p]} \
                l=$nt${p:+\$p;} p=$nt$p${p:+;}n
        do      case    $1      in
                (''|0*|*[!0-9]*?|*[!pd])
                   >&2  printf '"%s":\tARG ERR\n' "$1"
                        kill -2 0;;
                (1?|?|$((c-=1))) ! :; esac         &&
                printf  "\n:$n.0${l}N;%s$c;t$n.1%b" \
                        's/\n/&/' "$b$n.0\n:$n.1"
                shift;  printf %b "$p${1+\c}${nt}bt"
        done)"
}

It wouldn't be terribly difficult to do the other either, actually: you'd just just have to print so many [Nn]s at a sed to make it happen. So maybe I wasted my time writing the above. But it does work:

(   set -x
    seq 150 |
    altl p 40d p p 10d 5p
)

### This is the sed script the function's shell loop writes.
### We see it because I enabled -xtrace output on stderr.

:t
    p;n
:2.0
    N;s/\n/&/39;t2.1
    b2.0
:2.1
    n
    p;n
    p;n
:5.0
    N;s/\n/&/9;t5.1
    b5.0
:5.1
    n
:6.0
    $p;N;s/\n/&/4;t6.1
    b6.0
:6.1
    p;n
    bt

### This ends the sed script.
### The rest is sed's stdout.

1
42
43
54
55
56
57
58
59
100
101
112
113
114
115
116
117
11
  • Thanks! could you give me a few explanations?
    – Weiwei Sun
    Jul 8, 2015 at 22:00
  • 1
    sed 'N;N;p;N;N;N;d' is almost the same, but won't print a partial last group. Jul 8, 2015 at 22:00
  • @glennjackman - you can make it - but you have to use n for the lines you want to print, only buffer the deleted ones w/ N. Honestly, I should have gone there first (and did, actually, when I thought it was just every other - my first answer was sed n\;d), but then I did something I've been doing a lot lately. Sometimes you just get in a rut.
    – mikeserv
    Jul 8, 2015 at 22:13
  • 1
    With GNU sed, you could maybe use the first~last address notation e.g. sed -n '1~6 {N;N;p;}' Jul 9, 2015 at 1:13
  • Another solution with GNU sed: sed '4~6d; 5~6d; 6~6d'.
    – lcd047
    Jul 9, 2015 at 7:53

You must log in to answer this question.

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