Come mantenere solo ogni ennesima riga di un file


71

Ho un file CSV piuttosto considerevole (75 MB). Sto solo cercando di produrne un grafico, quindi non ho davvero bisogno di tutti i dati.

Riformulazione: vorrei eliminare n righe, quindi mantenere una riga, quindi eliminare n righe e così via.

Quindi se il file era simile al seguente:

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

e n = 2, quindi l'output sarebbe:

Line 3
Line 6

Sembra che sedpotrebbe essere in grado di farlo, ma non sono stato in grado di capire come. Un comando bash sarebbe l'ideale, ma sono aperto a qualsiasi soluzione.


2
Vuoi davvero le linee 1, 3, 6, ecc., Piuttosto che 1, 4, 7, ecc.?
Ilmari Karonen,

2
Dato che si tratta di un file CSV, suppongo che la prima riga contenga metadati (ovvero nomi di campi). In tal caso, la domanda dovrebbe essere "ogni ennesima riga dopo la prima".
iglvzx,

7
1, 3, 6 non ha ancora senso!
mercoledì

1
Immagino che dovrebbe essere 1, 3, 5 a meno che n = 2 sia un valore magico per numeri triangolari (1, 3, 6, 10, 15, 21 ecc.)
rjmunro

4
Puoi aggiornare la tua domanda per rendere coerente ciò che stai chiedendo ("ogni ennesima riga", "n = 2") e l'output desiderato (riga 3, riga 6)? I futuri lettori saranno confusi.
Keith Thompson

Risposte:


121
~ $ awk 'NR == 1 || NR % 3 == 0' yourfile
Line 1
Line 3
Line 6

NRLa variabile (numero di record) indica il numero di righe poiché il comportamento predefinito è una nuova riga per RS(separatore record). pattern e action sono opzionali nel formato predefinito di awk 'pattern {actions}'. quando diamo solo una parte del modello, allora awkscrive tutti i campi $0per le truecondizioni del nostro modello .


8
Grazie alle impostazioni predefinite, non hai nemmeno bisogno di così tanto:awk 'NR == 1 || NR % 3 == 0'
Kevin

@selman: se ti piace la soluzione di Kevin, potresti prendere in considerazione l'idea di aggiornare la tua risposta.
Keith Thompson

4
Ti interessa spiegare perché lo fa? In questo modo se qualcuno vuole modificarlo leggermente, quindi si spera che la tua spiegazione li aiuti a farlo
Ivo Flipse

Ho scoperto che questo approccio mi lascia intatte le linee 1 e 2. Ciò è confermato con il awk 'NR == 1 || NR % 2 == 0' myfile.txt | wc -lrisultato di un numero dispari mentre il file originale aveva un numero pari di righe. La risposta di @kev funziona meglio nel mio caso di test.
Daniel Da Cunha,

58

sed puoi anche fare questo:

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

man sedspiega ~come:

first ~ step Abbina ogni step della riga che inizia per prima con la riga. Ad esempio, `` sed -n 1 ~ 2p '' stamperà tutte le linee dispari nel flusso di input e l'indirizzo 2 ~ 5 corrisponderà ogni quinta riga, iniziando con la seconda. il primo può essere zero; in questo caso, sed funziona come se fosse uguale a step. (Questa è un'estensione.)


6
Potresti spiegare questo comando?
Qed

1
@qed Spiegazione: 1pstampa la prima riga, 0~3pstampa ogni terza riga a partire dalla riga 3 ( 1pè quindi necessario stampare la riga 1). Ma nota che 0~3non è standard ma un'estensione sed GNU.
Arkku,

"Questa è un'estensione." Quale versione stai / stavi usando?
Victor,

Questa risposta mi ha aiutato molto per Windows PowerShell. L'ho ampliato in questo modo: sed -n '1p;0~10p' '.\in.txt' > out.txtper stampare il file ridotto in un file di output.
kimliv,

22

Anche Perl può fare questo:

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

Questo programma stamperà la prima riga del suo input e successivamente ogni terza riga.

Per spiegarlo un po ', <>è l'operatore di input di riga, che scorre sulle righe di input quando viene utilizzato in un whileciclo come questo. La variabile speciale $.contiene il numero di righe lette finora ed %è l'operatore del modulo.

Questo codice può essere scritto in modo ancora più compatto come una riga, usando gli switch -ne -e:

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

L' -eopzione accetta un pezzo di codice Perl da eseguire come parametro della riga di comando, mentre l' -nopzione avvolge implicitamente il codice in un whileciclo come quello mostrato sopra.


Modifica: Per effettivamente ottenere linee 1, 3, 6, 9, ... come nell'esempio, piuttosto che le linee 1, 4, 7, 10, ... come ho pensato che volevi, sostituirlo $. % 3 == 1con $. == 1 or $. % 3 == 0.


7

Se vuoi farlo con uno script Bash puoi provare:

#!/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";

Salvalo come "read_lines.sh" e ricorda di dare i permessi + x al file bash.

chmod +x ./read_lines.sh

1
Se hai fatto questo emetti solo su standard out, leggi il no di righe per saltare dagli argomenti e leggi il file da standard in, sarebbe più semplice e più utile. Puoi ancora creare new_file.txt facendo ./read_lines.sh > new_file.txt.
rjmunro,

4

Una soluzione in puro bash, che non genera un 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

La prima riga salta 2 righe all'inizio del file e whilestampa la riga successiva e salta di nuovo 2 righe.

Se il tuo file è piccolo, questo è un modo molto efficiente di fare il lavoro in quanto non avvia un processo. Quando il file è di grandi dimensioni, seddovrebbe essere utilizzato in quanto è più efficiente nella gestione di io di bash.


1

Una versione di Python (sia Python 2 che Python 3):

python2 -c "print(''.join(open('file.txt').readlines()[::3]))"

sostituire [::3]con i parametri start, end e step size per un maggiore controllo. Ad esempio [10:36:5]mette le linee 10,15, ..., 35.

Nota, poiché readlines()mantiene le terminazioni di linea, l'output di questa chiamata potrebbe terminare con un'ultima riga vuota, a meno che l'ultima riga originale non venga emessa dalla dimensione del passo scelta.

È anche possibile una versione stream (qui output solo dopo lo stream finito):

python -c "import sys;print(''.join(list(sys.stdin)[::3]))" < file.txt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.