Como manter apenas toda enésima linha de um arquivo

60

Eu tenho um arquivo CSV bastante considerável (75MB). Eu só estou tentando produzir um gráfico disso, então eu realmente não preciso de todos os dados.

Reescrita: gostaria de excluir n linhas, manter uma linha, excluir n linhas e assim por diante.

Então, se o arquivo for assim:

Line 1
Line 2
Line 3
Line 4
Line 5
Line 6

e n = 2, então a saída seria:

Line 3
Line 6

Parece que sed pode fazer isso, mas não consegui descobrir como. Um comando bash seria ideal, mas estou aberto a qualquer solução.

    
por Computerish 03.03.2012 / 18:20

5 respostas

107
~ $ awk 'NR == 1 || NR % 3 == 0' yourfile
Line 1
Line 3
Line 6
A variável

NR (number of records) é o número de registros das linhas, porque o comportamento padrão é uma nova linha para RS (record seperator). o padrão e a ação são opcionais no formato padrão do awk 'pattern {actions}' . quando damos apenas uma parte do padrão, então awk escreve todos os campos $0 para as condições true do nosso padrão.

    
por 03.03.2012 / 19:13
51

sed também pode fazer isso:

$ sed -n '1p;0~3p' input.txt
Line 1
Line 3
Line 6

man sed explica ~ como:

first~step Match every step'th line starting with line first. For example, ''sed -n 1~2p'' will print all the odd-numbered lines in the input stream, and the address 2~5 will match every fifth line, starting with the second. first can be zero; in this case, sed operates as if it were equal to step. (This is an extension.)

    
por 03.03.2012 / 19:20
18

Perl também pode fazer isso:

while (<>) {
    print  if $. % 3 == 1;
}

Este programa irá imprimir a primeira linha de sua entrada, e a cada terceira linha depois.

Para explicar um pouco, <> é o operador de entrada de linha, que itera sobre as linhas de entrada quando usado em um loop while como este. A variável especial $. contém o número de linhas lidas até o momento e % é o operador de módulo.

Esse código pode ser escrito de forma ainda mais compacta como uma linha única, usando as opções -n e -e :

perl -ne 'print if $. % 3 == 1'  < input.txt  > output.txt

A opção -e usa um código Perl para executar como um parâmetro de linha de comando, enquanto a opção -n envolve implicitamente o código em um loop while como o mostrado acima.

Edit: Para obter as linhas 1, 3, 6, 9, ... como no exemplo, em vez das linhas 1, 4, 7, 10, ... como eu assumi que você queria, substitua $. % 3 == 1 com $. == 1 or $. % 3 == 0 .

    
por 03.03.2012 / 19:56
7

Se você quiser fazer isso com um script Bash , tente:

#!/bin/sh

echo Please enter the file name
read fname
echo Please enter the Nth lines that you want to keep
read n

exec<$fname
value=0
while read line
do
    if [ $(( $value % $n )) -eq 0 ] ; then
        echo -e "$line" >> new_file.txt
    fi
        let value=value+1 
done
echo "Check the 'new_file.txt' that has been created in this directory";

Salve como "read_lines.sh" e lembre-se de dar permissões de + x ao arquivo bash.

chmod +x ./read_lines.sh
    
por 03.03.2012 / 19:33
4

Uma solução em bash puro, que não gera um processo é:

{ for f in {1..2}; do read line; done;
  while read line; do
    echo $line;
    for f in {1..2}; do read line; done;
  done; } < file

A primeira linha pula 2 linhas no começo do arquivo, e a while imprime a próxima linha e pula 2 linhas novamente.

Se o seu arquivo for pequeno, essa é uma maneira muito eficiente de executar o trabalho, pois ele não inicia um processo. Quando seu arquivo é grande, sed deve ser usado, pois é mais eficiente em lidar com io do que bash .

    
por 15.03.2012 / 13:04

Tags