Com sed
, você pode fazer:
sed '24q;1,5d;12,18d' <infile >outfile
... Possivelmente, uma solução mais eficiente poderia ser obtida com head
. Don já demonstrou como isso pode funcionar muito bem, mas também estou brincando com ele. Algo que você pode fazer para lidar com esse caso específico:
for n in 5 6 7 6
do head -n"$n" >&"$((1+n%2))"
done <infile >outfile 2>/dev/null
... que chamaria head
4 vezes gravando outfile
ou /dev/null
dependendo se o valor da iteração para $n
é um número par ou ímpar.
Para casos mais gerais, juntei tudo isso de outras coisas que eu já tinha:
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
Isso pode fazer o que você gosta:
seq 100 | somehead -1 -5 6 -7 6
... que imprime ...
6
7
8
9
10
11
19
20
21
22
23
24
Ele espera que seu primeiro argumento seja uma contagem de repetições prefixada com um -
ou, na sua falta, apenas um -
. Se uma contagem for fornecida, ela repetirá o padrão de linha fornecido nos seguintes argumentos quantas vezes forem especificadas e parará assim que tiver feito isso.
Para cada arg que segue, ele interpretará um inteiro negativo para indicar uma contagem de linhas que deve ser escrita em /dev/null
e um inteiro positivo para indicar uma contagem de linhas que deve ser escrita em stdout
.
Portanto, no exemplo acima, as primeiras 5 linhas são impressas para /dev/null
, as próximas 6 para stdout
, as próximas 7 para /dev/null
novamente e as próximas 6 novamente para stdout
. Tendo atingido o último dos seus args e totalmente percorrido a contagem de repetição -1
, ele é encerrado. Se o primeiro argumento tivesse sido -2
, teria repetido o processo mais uma vez, ou se -
pelo tempo que pudesse.
Para cada ciclo de arg, o loop while
é processado uma vez. Na parte superior de cada loop, a primeira linha de stdin
é lida na variável $l
da shell. Isso é necessário porque while head </dev/null; do :; done
será repetido indefinidamente - head
indica em seu retorno quando atingiu o final do arquivo. Portanto, a verificação em relação a EOF é dedicada a read
e printf
escreverá $l
mais uma nova linha para stdout
apenas se o segundo argumento for um inteiro positivo.
A verificação de read
dificulta um pouco o loop, porque imediatamente após outro loop é chamado - um loop for
que itera sobre args 2-$#
como representado em $n
para cada iteração de seu loop while
pai. Isso significa que, para cada iteração, o primeiro argumento deve ser decrementado em um do valor especificado na linha de comando, mas todos os outros devem manter seus valores originais e, portanto, o valor do $_n
marker var é subtraído de cada um. sempre contém um valor maior que 0 para o primeiro argumento.
Isso constitui o loop principal da função, mas a maior parte do código está no topo e destina-se a permitir que a função limpe até mesmo um pipe como entrada. Isso funciona primeiro chamando um dd
em segundo plano para copiar seu arquivo tmp na saída em blocos de 4k por peça. A função então configura um loop de espera - que quase nunca deve completar um único ciclo completo - apenas para assegurar que dd
tenha feito pelo menos uma única gravação no arquivo antes que a função substitua seu stdin por um descritor de arquivo ligado a o tmpfile e depois desassocia imediatamente o arquivo com rm
. Isso permite que a função processe o fluxo de forma confiável sem exigir traps ou de outra forma para limpeza - assim que a função liberar sua declaração no fd, o arquivo tmp deixará de existir porque seu único link do sistema de arquivos já foi removido.