With sed
you might do:
sed '24q;1,5d;12,18d' <infile >outfile
...Possibly a more efficient solution could be had with head
. Don has already demonstrated how that might work very well, but I've been playing around with it as well. Something you might do to handle this specific case:
for n in 5 6 7 6
do head -n"$n" >&"$((1+n%2))"
done <infile >outfile 2>/dev/null
...which would call head
4 times writing either to outfile
or to /dev/null
depending on whether that iteration's value for $n
is an even or odd number.
For more general cases, I cobbled this together from some other stuff I already had:
somehead()(
### call it like:
### somehead -[repeat] [-][numlines]* <infile >outfile
set -e -- "${1#-}" "$@" #-e for arg validation
r=; cd -- "${TMP:-/tmp}" #go to tmp
dd bs=4096 of="$$$$" <&4 2>&3 & #dd <in >tmpfile &bg
until [ -s "$$$$" ]; do :; done #wait while tmpfile empty
exec <"$$$$" 4<&-; rm "$$$$" #<tmpfile; rm tmpfile
[ "$3${1}0" -ne "$3${2#?}0" ] || #validate args - chk $1
shift "$(((r=-${1:--1})||1))"; shift #shift 1||2
while [ "$(((r+=(_n=1))-1))" -ne 0 ] && #while ! $rptmax &&
IFS= read -r l && # ! EOF &&
printf "%.$(($1>0?${#l}+1:0))s" "$l # ? printf do
"; do for n do [ "${n#-}" -gt 0 ] || exit #args all -[nums>0]
head "-n$((${n#-}-_n))" >&"$((n>(_n=0)?1:3))" #head -n?$1 >?[+-]
done; done #done and done
) 4<&0 3>/dev/null #4<for dd 3>for head
This can do your thing like:
seq 100 | somehead -1 -5 6 -7 6
...which prints...
6
7
8
9
10
11
19
20
21
22
23
24
It expects its first arg to be a repeat count prefixed with a -
, or, failing that, just a -
. If a count is provided it will repeat the line pattern given in the following args as many times as specified and stop as soon as it has done so.
For each arg that follows it will interpret a negative integer to indicate a line-count which should be written to /dev/null
and a positive integer to indicate a line count which should be written to stdout
.
So in the above example it prints the first 5 lines to /dev/null
, the next 6 to stdout
, the next 7 to /dev/null
again and the next 6 once again to stdout
. Having reached the last of its args and fully cycled through -1
repeat count, it then quits. If the first arg had been -2
it would have repeated the process one more time, or if -
for as long as it could.
For each arg cycle the while
loop is processed once through. At the top of each loop the first line from stdin
is read into the shell variable $l
. This is necessary because while head </dev/null; do :; done
will repeat indefinitely - head
does indicate in its return when it has reached end of file. So the check against EOF is dedicated to read
and printf
will write $l
plus a newline to stdout
only if the second argument is a positive integer.
The read
check complicates the loop a little because immediately after another loop is called - a for
loop which iterates over args 2-$#
as represented in $n
for each iteration of its parent while
loop. This means that for each iteration the first arg must be decremented by one from the value specified on the command line, but all others should retain their original values, and so the value of the $_n
marker var is subtracted from each, but only ever holds a value greater than 0 for the first arg.
That constitutes the main loop of the function, but the bulk of the code is at the top and is intended to enable the function to cleanly buffer even a pipe as input. This works by first calling a backgrounded dd
to copy its in to a tmpfile on output at blocksizes of 4k a piece. The function then sets up a hold loop - which should almost never complete even a single full cycle - just to ensure that dd
has made at least a single write to the file before the function then replaces its stdin with a file descriptor linked to the tmpfile and afterward immediately unlinks the file with rm
. This enables the function to reliably process the stream without requiring traps or otherwise for cleanup - as soon as the function releases it claim on the fd the tmpfile will cease to exist because its only named filesystem link has already been removed.
head
andtail
? If so, your solution is pretty much the best you can do. If you're allowed to use other programs,sed
orawk
might allow for nicer solutions (i.e. with fewer process invocations). – n.st Jan 27 '15 at 19:44>>
) by enclosing the two commands in parentheses to redirect their concatenated output:(head -11 file | tail -6; head -24 file | tail -6) > file1
. It really comes down to personal preference which is nicer. – n.st Jan 27 '15 at 19:49