it-swarm-eu.dev

Wie kann ich unter Unix einen vorgegebenen Zeilenbereich aus einer Textdatei extrahieren?

Ich habe einen ~ 23000-Zeilen-SQL-Dump, der mehrere Datenbanken enthält. Ich muss einen bestimmten Abschnitt dieser Datei (d. H. Die Daten für eine einzelne Datenbank) extrahieren und in eine neue Datei einfügen. Ich kenne sowohl die Start- als auch die Endzeilennummern der gewünschten Daten.

Kennt jemand einen Unix-Befehl (oder eine Reihe von Befehlen), um alle Zeilen aus einer Datei zwischen etwa Zeile 16224 und 16482 zu extrahieren und sie dann in eine neue Datei umzuleiten?

447
Adam J. Forster
sed -n '16224,16482p;16483q' filename > newfile

Aus dem sed Handbuch :

p - Drucken Sie den Musterbereich (zur Standardausgabe) aus. Dieser Befehl wird normalerweise nur in Verbindung mit der Befehlszeilenoption -n verwendet.

n - Wenn der automatische Druck nicht deaktiviert ist, drucken Sie den Musterbereich, und ersetzen Sie den Musterbereich durch die nächste Eingabezeile. Ob es gibt keine Eingabe mehr und sed beendet ohne Verarbeitung mehr Befehle.

q - Beenden Sie sed, ohne weitere Befehle oder Eingaben zu bearbeiten . Beachten Sie, dass der aktuelle Musterbereich gedruckt wird, wenn der automatische Druck nicht mit der Option -n deaktiviert ist.

und

Adressen in einem sed-Skript können in einer der folgenden Formen vorliegen:

Nummer Die Angabe einer Zeilennummer stimmt nur mit dieser Zeile in der Eingabe überein.

Ein Adressbereich kann durch Angabe von zwei Adressen angegeben werden getrennt durch ein Komma (,). Ein Adressbereich stimmt mit Zeilen überein, die mit .__ beginnen. Die erste Adresse stimmt überein und wird bis zur zweiten .__ fortgesetzt. Adressenübereinstimmung (einschließlich).

681
boxxar
sed -n '16224,16482 p' orig-data-file > new-file

Dabei sind 16224, 16482 die Startzeilennummer und die Endzeilennummer, einschließlich. Dies ist 1-indexiert. -n unterdrückt das Echo der Eingabe als Ausgabe, was Sie eindeutig nicht möchten; Die Zahlen geben den Zeilenbereich an, damit der folgende Befehl ausgeführt werden kann. Der Befehl p druckt die relevanten Zeilen aus.

195
JXG

Ganz einfach mit Kopf/Schwanz:

head -16482 in.sql | tail -258 > out.sql

mit sed:

sed -n '16482,16482p' in.sql > out.sql

awk verwenden:

awk 'NR>=10&&NR<=20' in.sql > out.sql
78
manveru

Sie könnten 'vi' und dann den folgenden Befehl verwenden:

:16224,16482w!/tmp/some-file

Alternative: 

cat file | head -n 16482 | tail -n 258

BEARBEITEN: - Um eine Erklärung hinzuzufügen, verwenden Sie head -n 16482 , um die ersten 16482 Zeilen anzuzeigen, und verwenden Sie tail -n 258 , um die letzten 258 Zeilen aus der ersten Ausgabe zu erhalten. 

25
Mark Janssen

Es gibt einen anderen Ansatz mit awk:

awk 'NR==16224, NR==16482' file

Wenn die Datei sehr groß ist, kann es gut sein, exit nach dem Lesen der letzten gewünschten Zeile zu verwenden. Auf diese Weise wird die Datei nicht bis zum Ende gelesen:

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file
20
fedorqui
Perl -ne 'print if 16224..16482' file.txt > new_file.txt
14
mmaibaum
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2
8
Cetra

sed -n '16224,16482p' < dump.sql

5
cubex
cat dump.txt | head -16224 | tail -258

sollte den Trick tun. Der Nachteil dieses Ansatzes besteht darin, dass Sie die Arithmetik durchführen müssen, um das Argument für das Endstück zu bestimmen und zu berücksichtigen, ob das "Dazwischen" die Endzeile enthalten soll oder nicht.

5
JP Lodine

Schnell und dreckig:

head -16428 < file.in | tail -259 > file.out

Wahrscheinlich nicht der beste Weg, aber es sollte funktionieren.

BTW: 259 = 16482-16224 + 1.

3
jan.vdbergh

Ich wollte gerade den Kopf/Schwanz-Trick posten, aber eigentlich würde ich wahrscheinlich nur Emacs zünden. ;-)

  1. esc-x goto-line ret 16224
  2. kennzeichen (ctrl-space)
  3. esc-x goto-line ret 16482
  4. esc-w

öffne die neue ausgabedatei, ctl-y speichere

Mal sehen, was los ist.

3
sammyo

Auch wir können dies in der Befehlszeile überprüfen:

cat filename|sed 'n1,n2!d' > abc.txt

Zum Beispiel:

cat foo.pl|sed '100,200!d' > abc.txt
2
Chinmoy Padhi

Verwenden von Ruby:

Ruby -ne 'puts "#{$.}: #{$_}" if $. >= 32613500 && $. <= 32614500' < GND.rdf > GND.extract.rdf
2
Carl Blakeley

Ich würde ... benutzen:

awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt

FNR enthält die Datensatznummer (Zeile) der Zeile, die aus der Datei gelesen wird.

2
Paddy3118

Ich habe ein Haskell-Programm mit dem Namen splitter geschrieben, das genau dies tut: habe ein meinen Release-Blogpost durchgelesen .

Sie können das Programm wie folgt verwenden:

$ cat somefile | splitter 16224-16482

Und das ist alles, was es gibt. Sie benötigen Haskell, um es zu installieren. Gerade:

$ cabal install splitter

Und du bist fertig. Ich hoffe, dass Sie dieses Programm nützlich finden.

2

Das könnte für Sie funktionieren (GNU sed):

sed -ne '16224,16482w newfile' -e '16482q' file

oder bash nutzen:

sed -n $'16224,16482w newfile\n16482q' file
1
potong

Ich habe ein kleines Bash-Skript geschrieben, das Sie von Ihrer Befehlszeile aus ausführen können, solange Sie den Pfad von PATH aktualisieren, um sein Verzeichnis einzuschließen (oder Sie können es in einem Verzeichnis ablegen, das bereits im Pfad enthalten ist).

Verwendung: $ pinch Dateiname-Anfangszeile

#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon

FILENAME=$1
START=$2
END=$3

ERROR="[PINCH ERROR]"

# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
    echo "$ERROR Need three arguments: Filename Start-line End-line"
    exit 1
fi

# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
    echo -e "$ERROR File does not exist. \n\t$FILENAME"
    exit 1
fi

# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
    echo -e "$ERROR Start line is greater than End line."
    exit 1
fi

# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
    echo -e "$ERROR Start line is less than 0."
    exit 1
fi

# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
    echo -e "$ERROR End line is less than 0."
    exit 1
fi

NUMOFLINES=$(wc -l < "$FILENAME")

# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
    echo -e "$ERROR End line is greater than number of lines in file."
    exit 1
fi

# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))

# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
    < "$FILENAME" head -n $END | tail -n +$START
else
    < "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi

# Success
exit 0
1
Nerdfighter

Ich wollte dasselbe von einem Skript mit einer Variablen machen und habe es erreicht, indem ich die Variable $ in Anführungszeichen setzte, um den Variablennamen vom p zu trennen:

sed -n "$first","$count"p imagelist.txt >"$imageblock"

Ich wollte eine Liste in verschiedene Ordner aufteilen, fand die erste Frage und beantwortete einen nützlichen Schritt. (Split-Befehl ist keine Option auf dem alten Betriebssystem, zu dem ich den Code portieren muss).

1
KevinY

Da wir über das Extrahieren von Textzeilen aus einer Textdatei sprechen, werde ich einen speziellen Fall anführen, in dem Sie alle Zeilen extrahieren möchten, die einem bestimmten Muster entsprechen. 

myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile

Druckt die [Data] -Zeile und die verbleibenden. Wenn Sie den Text von Zeile1 in das Muster einfügen möchten, geben Sie Folgendes ein: sed -n '1,/Data/p' meineDatei. Wenn Sie zwei Muster kennen (besser in Ihrem Text eindeutig sein), können sowohl die Anfangs- als auch die Endzeile des Bereichs mit Übereinstimmungen angegeben werden.

sed -n '/BEGIN_MARK/,/END_MARK/p' myfile
0
Kemin Zhou

Auf den Schultern von Boxxar stehend, gefällt mir das:

sed -n '<first line>,$p;<last line>q' input

z.B.

sed -n '16224,$p;16482q' input

Der $ bedeutet "letzte Zeile", so dass der erste Befehl sed alle Zeilen beginnend mit der Zeile 16224 und der zweite Befehl sed quit after printing line 16428 druckt. (Das Hinzufügen von 1 für den q- Bereich in der Lösung von boxxar scheint nicht erforderlich zu sein.)

Diese Variante gefällt mir, weil ich die Endzeilennummer nicht zweimal angeben muss. Und ich habe gemessen, dass die Verwendung von $ keine nachteiligen Auswirkungen auf die Leistung hat.

0
Tilman Vogel

Die -n in den akzeptierten Antworten funktionieren. Hier ist ein anderer Weg, falls Sie geneigt sind.

cat $filename | sed "${linenum}p;d";

Dies bewirkt Folgendes:

  1. pipe im Inhalt einer Datei (oder geben Sie den gewünschten Text ein).
  2. sed wählt die angegebene Zeile aus und druckt sie aus
  3. d ist zum Löschen von Zeilen erforderlich, ansonsten wird davon ausgegangen, dass alle Zeilen gedruckt werden. d. h. ohne d werden alle Zeilen, die von der ausgewählten Zeile gedruckt werden, zweimal gedruckt, da der $ {Leinensum} p-Teil den Ausdruck verlangt. Ich bin mir ziemlich sicher, dass -n im Grunde dasselbe macht wie das d hier.
0
ThinkBonobo