Wie kann man unter Linux bestimmte Zeilen aus einer Textdatei anzeigen?

85

Ich denke, jeder kennt die nützlichen Linux cmd-Dienstprogramme headund tail. headErmöglicht das Drucken der ersten X-Zeilen einer Datei, tailmacht dasselbe, druckt jedoch das Ende der Datei. Was ist ein guter Befehl, um die Mitte einer Datei zu drucken? so etwas wie middle --start 10000000 --count 20(drucke die 10'000'000. bis die 10'000'010. Zeile).

Ich bin auf der Suche nach etwas, das sich effizient mit großen Dateien befasst. Ich habe es versucht tail -n 10000000 | head 10und es ist schrecklich langsam.

Boaz
quelle
5
Mögliches Duplikat von serverfault.com/questions/101900/…
Kyle Brandt

Antworten:

111
sed -n '10000000,10000020p' filename

Möglicherweise können Sie dies ein wenig beschleunigen:

sed -n '10000000,10000020p; 10000021q' filename

In diesen Befehlen -nbewirkt die Option, dass seddas automatische Drucken des Musterbereichs unterdrückt wird. Der pBefehl "print [s] the current pattern space" und der qBefehl "Sofort das sed-Skript verlassen, ohne weitere Eingaben zu verarbeiten ..." Die Anführungszeichen stammen von der sed manSeite .

Übrigens Ihr Befehl

tail -n 10000000 filename | head 10

beginnt in der zehnmillionsten Zeile ab dem Ende der Datei, während Ihr "mittlerer" Befehl anscheinend am zehnmillionsten vom Anfang beginnt, was äquivalent wäre zu:

head -n 10000010 filename | tail 10

Das Problem ist, dass bei unsortierten Dateien mit Zeilen variabler Länge jeder Prozess die Zeilenumbrüche durchlaufen muss. Es gibt keine Möglichkeit, das zu verkürzen.

Wenn die Datei jedoch sortiert ist (z. B. eine Protokolldatei mit Zeitstempeln) oder Zeilen mit fester Länge enthält, können Sie die Datei anhand einer Byte-Position durchsuchen. Im Beispiel für eine Protokolldatei können Sie eine Binärsuche für einen bestimmten Zeitraum durchführen, wie dies in meinem Python-Skript hier * der Fall ist. Bei der Datei mit fester Datensatzlänge ist das ganz einfach. Sie suchen nur nach linelength * linecountZeichen in der Datei.

* Ich habe weiterhin die Absicht, ein weiteres Update für dieses Skript zu veröffentlichen. Vielleicht komme ich eines Tages dazu.

Dennis Williamson
quelle
Hier ist eine sedVersion von Charles' middleFunktion: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Es werden mehrere Dateiargumente, Dateinamen mit Leerzeichen usw. verarbeitet. Mehrere Dateien werden zusammen verarbeitet, als wären sie auf die gleiche Weise wie sednormalerweise cattiert worden (also würden sich mittlere 1000 100 Datei1 Datei2 über das Ende der ersten Datei bis zum Anfang erstrecken des zweiten, wenn der erste weniger als 1100 Zeilen hat).
Dennis Williamson
Die Funktion in meinem vorherigen Kommentar kann mit einem Dateinamenparameter aufgerufen werden: middle startline count filenameoder mehreren Dateinamen: middle startline count file1 file2 file3oder mit Umleitung: middle startline count < filenameoder in einer Pipe: some_command | mittlere Startzeilenzahl` odercat file* | middle startline count
Dennis Williamson
Sollte das `in deinem sed Kommando nicht ein 'sein? Ich kann es nicht mit dem Backtick zum Laufen bringen, aber es funktioniert gut mit dem einfachen Anführungszeichen.
Ian Hunter
@beanland: Ja, es ist ein Tippfehler. Ich habe es behoben. Vielen Dank.
Dennis Williamson
1
@kev: Ich habe meiner Antwort eine Erklärung hinzugefügt.
Dennis Williamson
28

Ich fand die folgende Verwendung von sed

sed -n '10000000,+20p'  filename

Hoffe, es ist nützlich für jemanden!

Dox
quelle
Gut zu wissen, dass es eine Alternative zum letzten von Dennis vorgeschlagenen Zeilenargument gibt: eine Zeilenanzahl als zweites sed -nArgument, die es gut lesbar macht.
user3123159
Eine Beispielverwendung: extract_lines(){sed -n "$1,+$2p" <file>}die nach stdout schreibt.
User3123159
4

Dies ist mein erstes Mal hier! Wie auch immer, dieser ist einfach. Angenommen, Sie möchten die Zeile 8872 aus der Datei file.txt ziehen. So machen Sie es:

cat -n file.txt | grep '^ * 8872'

Jetzt geht es darum, danach 20 Zeilen zu finden. Um dies zu erreichen, tun Sie es

cat -n file.txt | grep -A 20 '^ * 8872'

Zeilen um oder davor finden Sie in den Flags -B und -C im grep-Handbuch.

Dennis
quelle
Das ist zwar technisch korrekt und eine interessante Methode, um eine Datei mit einer angemessenen Größe zu bearbeiten, aber ich bin gespannt auf ihre Wirksamkeit, wenn ich mit Dateien der Größe arbeite, nach der das Poster fragt.
Jenny D
Mehrere Zeilen: cat -n file.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +"
Jeffrey Knight
cat -n file.txt | grep '^ *1'Ergib alle Linien, die 1 auf ihrer rechten Seite haben. Wie wird Zeile 1 mit dieser Technik ausgegeben? Ich weiß ich kann -n 1 ... aber wie benutzt man grep?
Sean87
1

Die Antwort von Dennis ist der richtige Weg. Aber mit nur Kopf & Schwanz unter Bash:

middle () {head -n $ [$ 1 + $ 2] | Schwanz -n $ 2; }

Dies scannt die ersten $ 1 + $ 2 Zeilen zweimal und ist somit viel schlechter als Dennis 'Antwort. Aber Sie müssen sich nicht all diese sed Buchstaben merken, um es zu benutzen ...

Charles Stewart
quelle
Verwenden $[...]ist veraltet, zumindest in Bash. Außerdem fehlt ein Dateiparameter.
Dennis Williamson
@Dennis: Kein fehlender Parameter: Du solltest dies laut Standard verwenden middle 10 10 < /var/log/auth.log.
Charles Stewart
1

Verwenden Sie den folgenden Befehl, um den bestimmten Zeilenbereich abzurufen

awk 'NR < 1220974{next}1;NR==1513793{exit}' debug.log | tee -a test.log

Hier ist debug.log meine Datei, die aus einem Mangel an Zeilen besteht, und ich habe verwendet, um die Zeilen von 1220974 Zeilennummer bis 1513793 in eine Datei test.log zu drucken. Ich hoffe, es wird hilfreich sein, um den Zeilenbereich zu erfassen.

Neuling13
quelle
Dieselbe Antwort wie serverfault.com/a/641252/140016 . Abgestimmt.
Deer Hunter
Es ist nicht die gleiche Antwort. Bei großen Dateien sollte dies schneller sein, da der Vorgang nach dem Drucken der letzten Zeile tatsächlich abgebrochen wird, anstatt das Durchsuchen der Datei fortzusetzen.
Phobic
0

Eine rubinrote Oneliner-Version.

ruby -pe 'next unless $. > 10000000 && $. < 10000020' < filename.txt

Es kann für jemanden nützlich sein. Die Lösungen mit "sed" von Dennis und Dox sind sehr schön, auch weil es schneller zu sein scheint.

Shardan
quelle
0

Sie können 'nl' verwenden.

nl filename | grep <line_num>
Ajay
quelle
0

Zum Beispiel druckt diese awk Zeilen zwischen 20 und 40

awk '{if ((NR> 20) && (NR <40)) print $ 0}' / etc / passwd

Hrvoje Špoljar
quelle
0

Wenn Sie die Zeilennummern kennen, sagen Sie, Sie möchten die Zeilen 1, 3 und 5 aus einer Datei erhalten, sagen Sie / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
Dagelf
quelle
0

Perl ist König:

perl -ne 'print if ($. == 10000000 .. $. == 10000020)' filename
Peter V. Mørch
quelle