Ich habe eine Datei mit etwa 1000 Zeilen. Ich möchte, dass der Teil meiner Datei hinter der Zeile steht, die meiner grep-Anweisung entspricht.
d.h.
$ cat file | grep 'TERMINATE' // Its found on line 534
Also möchte ich die Datei aus Zeile 535 to line 1000
zur weiteren Verarbeitung.
Wie mache ich es ?
Folgendes druckt die Zeile, die mit TERMINATE
bis zum Ende der Datei übereinstimmt:
sed -n -e '/TERMINATE/,$p'
Erklärt: -n
deaktiviert das Standardverhalten von sed
des Druckens jeder Zeile nach Ausführung des Skripts. -e
hat ein Skript für sed
angegeben. /TERMINATE/,$
ist eine Adressbereichswahl, die die erste Zeile angibt, die dem regulären Ausdruck TERMINATE
entspricht (wie grep) bis zum Ende der Datei ($
) und p
ist der Druckbefehl, der die aktuelle Zeile druckt.
Dies wird von der Zeile gedruckt, die auf die Zeile folgt, die mit TERMINATE
bis zum Ende der Datei übereinstimmt:
(von NACH der übereinstimmenden Zeile zu EOF, NICHT einschließlich der übereinstimmenden Zeile)
sed -e '1,/TERMINATE/d'
Erklärt: 1,/TERMINATE/
ist eine Adressbereichsauswahl (Zeile), die die erste Zeile für die Eingabe in die erste Zeile angibt, die dem regulären Ausdruck TERMINATE
entspricht, und d
ist der Löschbefehl, der die aktuelle Zeile löscht und zur nächsten Zeile springt. Da sed
das Standardverhalten beim Drucken der Zeilen ist, werden die Zeilen nach TERMINATE
bis zum Ende der Eingabe gedruckt.
Bearbeiten:
Wenn Sie die Zeilen vor TERMINATE
haben wollen:
sed -e '/TERMINATE/,$d'
Und wenn Sie beide Zeilen vor und nach TERMINATE
in 2 verschiedenen Dateien in einem einzigen Durchgang haben möchten:
sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file
Die Vorher- und Nachher-Dateien enthalten die Zeile mit terminate. Um sie zu verarbeiten, benötigen Sie:
head -n -1 before
tail -n +2 after
Edit2:
WENN Sie die Dateinamen im sed-Skript nicht hartcodieren möchten, können Sie:
before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file
Dann müssen Sie jedoch den Code $
für die letzte Zeile kennzeichnen, damit die Shell nicht versucht, die Variable $w
zu erweitern (beachten Sie, dass wir jetzt das Skript anstelle von einfachen Anführungszeichen in doppelte Anführungszeichen setzen).
Ich habe vergessen zu sagen, dass die neue Zeile nach den Dateinamen im Skript wichtig ist, damit sed weiß, dass die Dateinamen enden.
Edit: 2016-0530
Sébastien Clément fragte: "Wie würden Sie die hartcodierte Variable TERMINATE
durch eine Variable ersetzen?"
Sie würden eine Variable für den übereinstimmenden Text erstellen und dann wie im vorherigen Beispiel machen:
matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file
so verwenden Sie eine Variable für den übereinstimmenden Text in den vorherigen Beispielen:
## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"
Die wichtigsten Punkte zum Ersetzen von Text durch Variablen in diesen Fällen sind:
$variablename
), die in single quotes
['
] eingeschlossen sind, werden nicht "erweitert", aber Variablen in double quotes
["
] werden es. Sie müssen also alle single quotes
in double quotes
ändern, wenn sie Text enthalten, den Sie durch eine Variable ersetzen möchten. sed
-Bereiche enthalten auch einen $
und werden unmittelbar gefolgt von einem Buchstaben wie: $p
, $d
, $w
. Sie sehen auch aus wie Variablen, die erweitert werden sollen. Daher müssen Sie diese $
-Zeichen mit einem umgekehrten Schrägstrich [\
] wie folgt ausdrücken: \$p
, \$d
, \$w
.Als einfache Annäherung könnten Sie verwenden
grep -A100000 TERMINATE file
was für TERMINATE
greift und bis zu 100000 Zeilen nach dieser Zeile ausgibt.
Von der Manpage
-A NUM, --after-context=NUM
NUM-Zeilen mit nachfolgendem Kontext nach übereinstimmenden Zeilen drucken. Platziert eine Zeile mit einem Gruppentrennzeichen (-) zwischen benachbarte Gruppen von Spielen. Mit -o oder --only passend Option hat dies keine Auswirkung und es wird eine Warnung ausgegeben.
Ein Werkzeug, das Sie hier verwenden können, ist awk:
cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1} {if (found) print }'
Wie funktioniert das:
Die anderen Lösungen benötigen möglicherweise viel Speicher, wenn Sie sie für sehr große Dateien verwenden.
Verwenden Sie die Bash-Parametererweiterung wie folgt:
content=$(cat file)
echo "${content#*TERMINATE}"
Wenn ich Ihre Frage richtig verstehe, möchten Sie die Zeilen afterTERMINATE
und nicht die TERMINATE
- Zeile. awk
kann dies auf einfache Weise tun:
awk '{if(found) print} /TERMINATE/{found=1}' your_file
Erläuterung:
if(found) print
) druckt also nichts, um damit zu beginnen.Dadurch werden alle Zeilen after der TERMINATE
-Zeile gedruckt.
Verallgemeinerung:
Beispiel:
$ cat ex_file.txt
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt
A good line to include
And this line
Yep
$
Erläuterung:
found
eingestellt ist.found=1
so, dass die folgenden Zeilen gedruckt werden. Beachten Sie, dass diese Prüfung after des eigentlichen Drucks ausgeführt wird, um die Start- Zeile vom Ergebnis auszuschließen.Anmerkungen:
BEGIN{found=0}
hinzufügen können.Wenn Sie aus irgendeinem Grund die Verwendung von sed vermeiden möchten, wird die Zeile mit TERMINATE
bis zum Ende der Datei gedruckt:
tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file
und Folgendes wird von der folgenden Zeile bis zum Ende der Datei mit TERMINATE
gedruckt:
tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file
Sed benötigt zwei Prozesse, und wenn sich die Datei zwischen Ausführung von grep und tail ändert, kann das Ergebnis inkohärent sein. Wenn die Datei keine TERMINATE
enthält, schlägt der 1. Befehl fehl.
Es gibt viele Möglichkeiten, dies mit sed
oder awk
zu tun:
sed -n '/TERMINATE/,$p' file
Dies sucht nach TERMINATE
in Ihrer Datei und druckt von dieser Zeile bis zum Ende der Datei.
awk '/TERMINATE/,0' file
Dies ist genau das gleiche Verhalten wie sed
.
Wenn Sie die Nummer der Zeile kennen, von der aus Sie den Druck starten möchten, können Sie sie zusammen mit NR
angeben (Nummer des Datensatzes, die schließlich die Nummer der Zeile angibt):
awk 'NR>=535' file
$ seq 10 > a #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10
grep -A 10000000 'TERMINATE' Datei
Dies könnte eine Möglichkeit sein. Wenn Sie wissen, welche Zeile der Datei Ihr grep-Word enthält und wie viele Zeilen Sie in Ihrer Datei haben:
grep -A466 'TERMINATE'-Datei
Alternativen zur exzellenten sed
-Antwort von jfgagne, die keine passende Zeile enthält:
awk '/TERMINATE/ {y=1;next} y'
( https://stackoverflow.com/a/18166628 )awk '/TERMINATE/ ? c++ : c'
( https://stackoverflow.com/a/23984891 )Perl -ne 'print unless 1 .. /TERMINATE/'
( https://stackoverflow.com/a/18167194 )