So behalten Sie nur jede n-te Zeile einer Datei bei


71

Ich habe eine ziemlich große CSV-Datei (75 MB). Ich versuche nur, ein Diagramm davon zu erstellen, daher brauche ich wirklich nicht alle Daten.

Umformulierung: Ich möchte n Zeilen löschen, dann eine Zeile behalten, dann n Zeilen löschen und so weiter.

Also, wenn die Datei so aussah:

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

und n = 2, dann wäre die Ausgabe:

Line 3
Line 6

Es scheint, als sedkönnte dies möglich sein, aber ich konnte nicht herausfinden, wie. Ein Bash-Befehl wäre ideal, aber ich bin offen für jede Lösung.


2
Möchten Sie wirklich die Zeilen 1, 3, 6 usw. anstatt 1, 4, 7 usw.?
Ilmari Karonen

2
Da es sich um eine CSV-Datei handelt, gehe ich davon aus, dass die erste Zeile Metadaten (dh Feldnamen) enthält. In diesem Fall sollte die Frage "jede n-te Zeile nach der ersten" lauten.
Iglvzx

7
1, 3, 6 macht immer noch keinen Sinn!
Wim

1
Ich denke, es sollte 1, 3, 5 sein, es sei denn, n = 2 ist ein magischer Wert für Dreieckszahlen (1, 3, 6, 10, 15, 21 usw.)
rjmunro

4
Können Sie Ihre Frage aktualisieren, um sicherzustellen, dass das, wonach Sie fragen ("jede n-te Zeile", "n = 2") und Ihre gewünschte Ausgabe (Zeile 3, Zeile 6) konsistent sind? Zukünftige Leser werden verwirrt sein.
Keith Thompson

Antworten:


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

NRDie Variable (Anzahl der Datensätze) gibt die Anzahl der Datensätze an, da das Standardverhalten die neue Zeile für RS(Datensatztrenner) ist. Muster und Aktion sind im Standardformat von awk optional 'pattern {actions}'. Wenn wir nur einen Musterteil awkangeben, werden alle Felder $0für die trueBedingungen unseres Musters geschrieben .


8
Dank der Standardeinstellungen brauchen Sie nicht einmal so viel:awk 'NR == 1 || NR % 3 == 0'
Kevin

@selman: Wenn Sie Kevins Lösung mögen, sollten Sie Ihre Antwort aktualisieren.
Keith Thompson

4
Möchten Sie erklären, warum dies so ist? Auf diese Weise hilft ihnen hoffentlich Ihre Erklärung, wenn jemand etwas daran arbeiten möchte
Ivo Flipse

Ich stellte fest, dass dieser Ansatz die Zeilen 1 und 2 unberührt lässt. Dies wird mit awk 'NR == 1 || NR % 2 == 0' myfile.txt | wc -leiner ungeraden Zahl bestätigt, während die ursprüngliche Datei eine gerade Anzahl von Zeilen hatte. @kev Antwort funktioniert am besten in meinem Testfall.
Daniel Da Cunha

58

sed kann das auch machen:

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

man sederklärt ~als:

first ~ step Stimmt mit jeder Zeile überein, die mit der ersten Zeile beginnt. Beispiel: "sed -n 1 ~ 2p" gibt alle ungeraden Zeilen im Eingabestream aus, und die Adresse 2 ~ 5 stimmt mit jeder fünften Zeile überein, beginnend mit der zweiten. Erstens kann Null sein. sed verhält sich in diesem Fall so, als wäre es gleich step. (Dies ist eine Erweiterung.)


6
Könnten Sie diesen Befehl erklären?
Qed

1
@qed Erklärung: 1pdruckt die erste Zeile, 0~3pdruckt jede dritte Zeile ab Zeile 3 (dies 1pist also erforderlich, um Zeile 1 zu drucken). Beachten Sie jedoch, dass dies 0~3nicht der Standard ist, sondern eine GNU sed-Erweiterung.
Arkku

"Dies ist eine Erweiterung." Welche Version haben / haben Sie verwendet?
Victor

Diese Antwort hat mir für Windows PowerShell sehr geholfen. Ich habe es so erweitert: sed -n '1p;0~10p' '.\in.txt' > out.txtum die verkleinerte Datei in eine Ausgabedatei zu drucken.
Kimliv

22

Perl kann das auch:

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

Dieses Programm gibt die erste Zeile seiner Eingabe und danach jede dritte Zeile aus.

Um es ein bisschen zu erklären, <>ist der Zeileneingabeoperator, der über die Eingabezeilen iteriert, wenn er in einer whileSchleife wie dieser verwendet wird. Die spezielle Variable $.enthält die Anzahl der bisher gelesenen Zeilen und %ist der Moduloperator.

Mit den Schaltern -nund kann dieser Code als Einzeiler noch kompakter geschrieben -ewerden:

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

Der -eSchalter benötigt einen Teil des Perl-Codes, um als Befehlszeilenparameter ausgeführt zu werden, während der -nSchalter den Code implizit in eine whileSchleife wie die oben gezeigte einschließt.


Bearbeiten: Um tatsächlich die Zeilen 1, 3, 6, 9, ... wie im Beispiel zu erhalten, anstelle der Zeilen 1, 4, 7, 10, ..., wie ich zuerst angenommen habe, dass Sie es wollten, ersetzen Sie sie $. % 3 == 1durch $. == 1 or $. % 3 == 0.


7

Wenn Sie es mit einem Bash- Skript machen möchten, können Sie versuchen:

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

Speichern Sie es als "read_lines.sh" und denken Sie daran, der Bash-Datei + x-Berechtigungen zu erteilen.

chmod +x ./read_lines.sh

1
Wenn Sie dies einfach auf standard out ausgeben, die Anzahl der Zeilen lesen, um die Argumente zu überspringen und die Datei von standard in zu lesen, wäre dies einfacher und nützlicher. Sie könnten immer noch new_file.txt machen, indem Sie tun ./read_lines.sh > new_file.txt.
rjmunro

4

Eine Lösung in Pure Bash, die keinen Prozess hervorbringt, ist:

{ 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

Die erste Zeile überspringt 2 Zeilen am Anfang der Datei, und whiledie nächste Zeile wird gedruckt, und es werden 2 Zeilen erneut übersprungen.

Wenn Ihre Datei klein ist, ist dies eine sehr effiziente Methode, um den Job auszuführen, da kein Prozess gestartet wird. Wenn Ihre Datei groß ist, sedsollten Sie sie verwenden, da sie effizienter mit io umgeht als bash.


1

Eine Python-Version (sowohl Python 2 als auch Python 3):

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

Ersetzen Sie sie [::3]durch Start-, End- und Schrittgrößenparameter, um mehr Kontrolle zu haben. ZB [10:36:5]Zeilen 10, 15, ..., 35.

Da readlines()die Zeilenenden beibehalten werden, endet die Ausgabe dieses Aufrufs möglicherweise mit einer leeren letzten Zeile, es sei denn, die ursprüngliche letzte Zeile wird mit der ausgewählten Schrittgröße ausgegeben.

Auch eine Stream-Version ist möglich (hier erst nach beendetem Stream ausgeben):

python -c "import sys;print(''.join(list(sys.stdin)[::3]))" < file.txt
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.