22 Stimmen

Eine Zeile in einer großen Datei mit der Bash ermitteln

Wie kann ich eine bestimmte Zeile in einer 3 Gigabyte großen Textdatei finden? Alle Zeilen haben:

  • die gleiche Länge, und
  • werden abgegrenzt durch \n .

Und ich muss in der Lage sein, jede beliebige Leitung auf Abruf zu erhalten.

Wie lässt sich das bewerkstelligen? Es muss nur eine Zeile zurückgegeben werden.

23voto

camh Punkte 38167

Wenn alle Zeilen die gleiche Länge haben, ist es bei weitem am besten, wenn Sie dd(1) und geben Sie ihm einen Skip-Parameter.

Lassen Sie die Blockgröße die Länge jeder Zeile (einschließlich des Zeilenumbruchs) sein, dann können Sie das tun:

$ dd if=filename bs=<line-length> skip=<line_no - 1> count=1 2>/dev/null

Die Idee ist, alle vorherigen Zeilen zu überspringen ( skip=<line_no - 1> ) und eine einzelne Zeile lesen ( count=1 ). Da die Blockgröße auf die Zeilenlänge eingestellt ist ( bs=<line-length> ), ist jeder Block effektiv eine einzelne Zeile. Leiten Sie stderr um, damit Sie am Ende nicht die lästigen Statistiken erhalten.

Das sollte viel effizienter sein, als die Zeilen vor der gewünschten Zeile durch ein Programm zu streamen, das alle Zeilen liest und sie dann wegwirft, wie dd sucht die gewünschte Position in der Datei und liest nur eine Zeile der Daten aus der Datei.

16voto

Paul Creasey Punkte 27679

head -10 file | tail -1 Die Rückgabe der Linie 10 ist wahrscheinlich langsam.

de aquí

# print line number 52 
sed -n '52p' # method 1 
sed '52!d' # method 2 
sed '52q;d' # method 3, efficient on large files

4voto

Jamie Punkte 2145

Eine awk-Alternative, wobei 3 die Zeilennummer ist.

awk 'NR == 3 {print; exit}' file.txt

3voto

paxdiablo Punkte 809679

Wenn es sich nicht um eine Datei mit fester Rücklauflänge handelt und Sie keine Indizierung der Zeilenanfänge vornehmen, ist es am besten, wenn Sie einfach die Option verwenden:

head -n N filespec | tail -1

donde N ist die gewünschte Zeilennummer.

Dies ist leider nicht der leistungsfähigste Code für eine 3 GB große Datei, aber es gibt Möglichkeiten, ihn zu verbessern.

Wenn sich die Datei nicht allzu oft ändert, sollten Sie eine Indizierung in Betracht ziehen. Damit meine ich, dass man otro Datei mit den Zeilenoffsets als Datensätze fester Länge.

Also die Datei:

0000000000
0000000017
0000000092
0000001023

ermöglicht es Ihnen, jede Zeile schnell zu finden. Multiplizieren Sie einfach die gewünschte Zeilennummer mit der Größe des Indexsatzes und suchen Sie bis dorthin in der Indexdatei.

Verwenden Sie dann den Wert an dieser Stelle, um in der Hauptdatei zu suchen, damit Sie bis zum nächsten Zeilenumbruch lesen können.

Für Zeile 3 würden Sie also bis 33 in der Indexdatei suchen (die Länge des Indexsatzes beträgt 10 Zeichen plus ein weiteres für den Zeilenumbruch). Dort wird der Wert gelesen, 0000000092 gibt Ihnen den Offset an, den Sie in der Hauptdatei verwenden müssen.

Natürlich ist das nicht so nützlich, wenn sich die Datei häufig ändert, aber wenn Sie kontrollieren können, was passiert, wenn Dinge angehängt werden, können Sie immer noch effizient Offsets zum Index hinzufügen. Wenn Sie nicht Wenn Sie das kontrollieren, müssen Sie immer dann neu indizieren, wenn das Datum der letzten Änderung des Indexes vor dem der Hauptdatei liegt.


Und, basierend auf Ihrem Update:

Update: Falls es von Bedeutung ist, alle Zeilen haben die gleiche Länge.

Mit dieser zusätzlichen Information brauchen Sie den Index nicht - Sie können einfach sofort an der richtigen Stelle in der Hauptdatei suchen, indem Sie die Datensatzlänge mit der Datensatzlänge multiplizieren (vorausgesetzt, die Werte passen zu Ihren Datentypen).

Also so etwas wie der Pseudocode:

def getline(fhandle,reclen,recnum):
    seek to position reclen*recnum for file fhandle.
    read reclen characters into buffer.
    return buffer.

2voto

Dennis Williamson Punkte 322329

Utilisez q をもって sed um die Suche zu beenden, nachdem die Zeile gedruckt worden ist.

sed -n '11723{p;q}' filename

Python (minimale Fehlerprüfung):

#!/usr/bin/env python
import sys

# by Dennis Williamson - 2010-05-08
# for http://stackoverflow.com/questions/2794049/getting-one-line-in-a-huge-file-with-bash

# seeks the requested line in a file with a fixed line length

# Usage: ./lineseek.py LINE FILE

# Example: ./lineseek 11723 data.txt

EXIT_SUCCESS      = 0
EXIT_NOT_FOUND    = 1
EXIT_OPT_ERR      = 2
EXIT_FILE_ERR     = 3
EXIT_DATA_ERR     = 4

# could use a try block here
seekline = int(sys.argv[1])

file = sys.argv[2]

try:
    if file == '-':
        handle = sys.stdin
        size = 0
    else:
        handle = open(file,'r')
except IOError as e:
    print >> sys.stderr, ("File Open Error")
    exit(EXIT_FILE_ERR)

try:
    line = handle.readline()
    lineend = handle.tell()
    linelen = len(line)
except IOError as e:
    print >> sys.stderr, ("File I/O Error")
    exit(EXIT_FILE_ERR)

# it would be really weird if this happened
if lineend != linelen:
    print >> sys.stderr, ("Line length inconsistent")
    exit(EXIT_DATA_ERR)

handle.seek(linelen * (seekline - 1))

try:
    line = handle.readline()
except IOError as e:
    print >> sys.stderr, ("File I/O Error")
    exit(EXIT_FILE_ERR)

if len(line) != linelen:
    print >> sys.stderr, ("Line length inconsistent")
    exit(EXIT_DATA_ERR)

print(line)

Die Validierung von Argumenten sollte viel besser sein, und es gibt Raum für viele andere Verbesserungen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X