Como selecionar certas linhas (n, n + 4, n + 8, n + 12…) do arquivo?

Respostas:

28

Usando o AWK:

awk '!((NR - 1) % 4)' input > output

Descobrir como isso funciona é deixado como um exercício para o leitor.

Stephen Kitt
fonte
obrigado por este curto curso awk!
Darxmurf
20
NR % 4 == 1seria IMO mais legível.
Stéphane Chazelas
12
Acordado @ Stéphane; esta é provavelmente questionável da minha parte, mas para potencialmente perguntas de casa Eu tento para ofuscar as minhas respostas um pouco ...
Stephen Kitt
@StephenKitt ofusca suas respostas? Verdade? Este não é o lugar para fazer isso.
data
22

Usando split (GNU coreutils):

split -nr/1/4 input > output
  • -ngerar CHUNKSarquivos de saída

e CHUNKScomo

  • r/K/N use a distribuição round robin e apenas produza Kth de N no stdout sem dividir linhas / registros
Freddy
fonte
1
Mente soprada. Respostas como essa são por que eu amo esse SE. Obrigado!
User1717828
21

Com o GNU sed:

sed '1~4!d' < input > output

Com padrão sed:

sed -n 'p;n;n;n' < input > output

Com 1e 4dentro $ne $ivariáveis:

sed "$n~$i!d" # GNU only
awk -v n="$n" -v i="$i" 'NR >= n && (NR % i) == (n % i)'
Stéphane Chazelas
fonte
7

Adicionando a solução perl obrigatória:

perl -ne 'print if $. % 4 == 1' input > output
Wurtel
fonte
4

Versão Python, apenas por diversão:

with open('input.txt') as f:
    for i, line in enumerate(f.readlines()):
        if i%4 == 0:
            print(line.strip())
user1717828
fonte
enumerate(f)deve ser capaz de fazer o trabalho enquanto consome menos memória
Iruvar
@iruvar Isso é tão legal! Nunca percebi isso antes; estará usando no futuro. Sinta-se livre para editá-lo nesta resposta; Eu realmente não vou manter isso com otimizações, já que as outras respostas do Bash (especialmente essa ) são definitivamente o caminho a seguir.
User1717828
Se você for usar readlines(portanto, colocar o arquivo inteiro na memória), use-o f.readlines()[::4]para obter cada quarta linha. Então você pode usar print(''.join(f.readlines()[::4])).
Nick Matteo
3

POSIX sed: esse método usa o posixly sed e pode ser executado em qualquer lugar ou pelo menos nos seds que respeitam o posix.

 $ sed -ne '
   /\n/!{
    H;s/.*//;x
   }

   :loop
       $bdone
       N;s/\n/&/4
       tdone
   bloop

   :done
   s/.//;P
 ' input.file

Outra é uma geração programática de código sed para fins de escalabilidade:

$ code=$(yes n | head -n 4 | paste -sd\; | sed s/n/p/)
$ sed -ne "$code" input.file

Perl: preenchemos o array A até ele ter 4 de tamanho. Em seguida, imprimimos seu primeiro elemento e também limpamos a matriz.

$ perl -pe '
   $A[@A] = @A ? <> : $_ while @A < 4;
   $_ = (splice @A)[0];
' input.file
Rakesh Sharma
fonte
1

Ligue com scriptname filename skip(4 no seu caso). Ele funciona puxando as iterlinhas da parte superior do arquivo e depois apenas produzindo a última. Em seguida, incrementos iterpor skipse repete, desde que o valor de iternão ultrapassou o linesno file.

#!/bin/bash
file="$1"
lines=`wc -l < "$file"`
skips="$2" || "4"
iter=1
while [ "$iter" -le "$lines" ]; do
 head "$file" -n $iter | tail -n 1
 iter=$(( $iter + $skips ))
done
Ryan Grange
fonte
1

Pure Bash:

mapfile -t lines < input
for (( i=0; i < ${#lines[@]}; i+=4 ))
do printf "%s\n" "${lines[$i]}"
done

O mapfile é um built -in adicionado no Bash 4 que lê a entrada padrão em uma matriz, chamada aqui lines, com uma linha por entrada. A -topção retira as novas linhas finais.

Se você deseja imprimir cada quarta linha começando na linha 4, é possível fazer isso em um comando usando mapfilea opção de retorno de chamada -C, que executa o código fornecido a cada tantas linhas, com o intervalo fornecido por -c. O índice atual da matriz e a próxima linha a ser atribuída são fornecidos ao código como argumentos.

mapfile -t -c4 -C 'printf "%.0s%s\n"' < input

Isso usa o printfbuiltin; o código de formato %.0ssuprime o primeiro argumento (o índice), portanto, apenas a linha é impressa.

Você pode usar o mesmo comando para imprimir a cada quarta linha, começando na linha 1, 2 ou 3, mas você precisará acrescentar 3, 2 ou 1 linhas inputantes de alimentá-lo mapfile, o que eu acho que é mais problemático do que vale a pena .

Isso também funciona:

mapfile -t lines < input
printf "%s%.0s%.0s%.0s\n" "${lines[@]}"

Aqui, printfconsome quatro entradas da matriz linespor vez, apenas imprimindo a primeira e pulando as outras três %.0s. Eu não gosto disso, já que você precisa mexer manualmente com a string de formato para diferentes intervalos ou pontos de partida.

Nick Matteo
fonte