Wie lösche ich die ersten n Zeilen und die letzte Zeile einer Datei mit Shell-Befehlen?


31

Ich habe eine Datei Element_querymit dem Namen, die das Ergebnis einer Abfrage enthält:

SQL> select count (*) from element;

[Output of the query which I want to keep in my file]

SQL> spool off;

Ich möchte die erste und letzte Zeile mit dem Shell-Befehl löschen.


2
Am besten reparieren Sie dies in SQL * Plus. Anstatt eine Datei zu generieren und dann zu versuchen, die nicht gewünschten Elemente zu kürzen, können Sie SQL * Plus einfach anweisen, diese Elemente zunächst nicht zu generieren. Ein Ansatz wird im Abschnitt "Flatfile erstellen" unter docs.oracle.com/cd/A84870_01/doc/sqlplus.816/a75664/ch44.htm beschrieben . Ein anderer Ansatz ist unter stackoverflow.com/q/2299375/978917 beschrieben .
Ruakh

Antworten:


48

GNU benutzen sed:

sed -i '1d;$d' Element_query

Wie es funktioniert :

  • -iOption bearbeiten Sie die Datei selbst. Sie können diese Option auch entfernen und die Ausgabe in eine neue Datei oder einen anderen Befehl umleiten, wenn Sie möchten.
  • 1dlöscht die erste Zeile ( 1nur auf die erste Zeile einwirken, dum sie zu löschen)
  • $dlöscht die letzte Zeile ( $um nur die letzte Zeile dzu bearbeiten, um sie zu löschen)

Weitergehen :

  • Sie können auch einen Bereich löschen. 1,5dWürde zum Beispiel die ersten 5 Zeilen löschen.
  • Sie können auch jede Zeile löschen, die mit SQL>der Anweisung beginnt/^SQL> /d
  • Sie können jede leere Zeile mit löschen /^$/d
  • Schließlich können Sie eine der Anweisungen kombinieren, indem Sie sie mit einem Semikolon ( statement1;statement2;satement3;...) trennen oder in der Befehlszeile ( -e 'statement1' -e 'statement 2' ...) separat angeben.

Wenn seine 3. Zeile zu löschen ... dann muss ich 3d anstelle von 1d verwenden? wenn seine 3. Zeile von der letzten bis zur Löschung ... was wird dann der Befehl sein?
Pmaipmui

Wie lösche ich die dritte Zeile der letzten Zeile mit Shell-Befehlen?
Pmaipmui

@Nainita Sie können einen Bereich angeben ( 1,3ddie ersten drei Zeilen werden gelöscht), das Ende ist jedoch etwas schwieriger. Je nachdem, was Sie möchten, können Sie dies besser nutzen: sed -i '/^SQL> /d' Element_queryum Zeilen zu löschen, die SQL> unabhängig davon beginnen, wo sie sich in der Datei befinden.
user43791

@Nainita - siehe meine Antwort hier für beliebigen Schwanz zählt - es bietet zwei Lösungen für das Abstreifen Zählung bis zum Ende der Datei Linien als relativ. Einer ist ein sedEinzeiler - mit dem beliebige Zeilenzahlen am Anfang und Ende einer Datei entfernt werden können. Besser ist es jedoch, eine einzelne Eingabe über zwei headProzesse zu gruppieren, solange es sich um eine reguläre Datei handelt schnellste Weg, dies normalerweise zu tun.
mikeserv

Ich habe sed -i '1d' table-backup.sqldie erste Zeile der SQL-Textdatei gelöscht
David Thomas

8

Kopf; Kopf

{   head -n[num] >/dev/null
    head -n[num]
}  <infile >outfile

Mit der obigen Option können Sie die erste Anzahl von Zeilen angeben, die mit dem ersten headBefehl vom Kopf der Ausgabe entfernt werden sollen , und die Anzahl von Zeilen, in die outfilemit dem zweiten Befehl geschrieben werden soll . Dies ist in der Regel auch schneller als sed- insbesondere bei umfangreichen Eingaben -, obwohl zwei Aufrufe erforderlich sind. Wo auf sedjeden Fall sollte jedoch bevorzugt wird, ist in dem Fall , dass <infileist nicht eine regelmäßige, lseekable Datei - denn dies wird in der Regel nicht arbeiten , wie in diesem Fall bestimmt, sondern sedkann alle Ausgänge Änderungen in einem einzigen Skript Prozess behandeln.

Mit einer GNU können headSie die -negative Form auch [num]im zweiten Befehl verwenden. In diesem Fall werden mit dem folgenden Befehl die erste und die letzte Zeile von der Eingabe entfernt:

{   head -n1 >/dev/null
    head -n-1
}  <infile >outfile

ODER mit einem POSIX sed:

Angenommen, ich habe eine Eingabe von 20 Zeilen gelesen und wollte die ersten 3 und die letzten 7 Zeilen entfernen. Wenn ich mich dazu entschließen sedwürde, würde ich dies mit einem Endpuffer tun. Ich würde zuerst drei und sieben addieren, um insgesamt zehn Streifen zu zählen, und dann tun:

seq 20 | sed -ne:n -e '3d;N;1,10bn' -eP\;D

Dies ist ein Beispiel, bei dem die ersten 3 und letzten 7 Zeilen von der Eingabe entfernt werden. Die Idee ist, dass Sie so viele Zeilen puffern können, wie Sie möchten, um das Ende der Eingabe im Musterbereich auf einem Stapel zu Pentfernen, aber nur die erste Zeile für jede eingezogene Zeile zu drucken.

  • Auf Zeilen 1,10 sed Pwird nichts gedruckt, da für jede Zeile die Eingabe zeilenweise in einer bRanch-Schleife im Musterraum gestapelt wird.
  • In der dritten Zeile wird der gesamte sedStapel dgelöscht - und so werden die ersten drei Zeilen auf einen Schlag von der Ausgabe entfernt.
  • Wenn seddie $letzte Eingabezeile erreicht ist und versucht, die Next zu ziehen, wird EOF gedrückt und die Verarbeitung wird vollständig gestoppt. Zu diesem Zeitpunkt enthält der Musterraum jedoch alle Linien, 14,20von denen noch keine Pgedruckt wurden und es niemals sind.
  • In jeder anderen Zeile sed Pwird nur bis zur ersten \newline im Musterbereich gedruckt und diese Dgelöscht, bevor ein neuer Zyklus mit den verbleibenden - oder den nächsten 6 Eingabezeilen beginnt. Die 7. Zeile wird mit dem NBefehl ext im neuen Zyklus wieder an den Stack angehängt .

Von der seqAusgabe (die 20 fortlaufend nummerierte Zeilen umfasst) werden also sednur folgende Daten ausgegeben :

4
5
6
7
8
9
10
11
12
13

Dies wird problematisch, wenn die Anzahl der Zeilen, die Sie vom Ende der Eingabe entfernen möchten, groß ist - da seddie Leistung direkt proportional zur Größe des Musterraums ist. Dennoch ist es in vielen Fällen eine praktikable Lösung - und POSIX gibt einen sedMusterbereich vor, der vor dem Busting mindestens 4 KB verarbeitet.


1
gnu tailunterstützt auch die erweiterte tail -n+<num>Syntax "start from line <num>"
UloPe

4

Ich werde nicht antworten, wie man eine Anzahl von Zeilen löscht. Ich werde das Problem folgendermaßen angreifen:

grep -v '#SQL>' Element_query >outfile

Anstatt Zeilen zu zählen, werden die SQL-Befehle durch Erkennen der Eingabeaufforderungen entfernt. Diese Lösung kann dann für andere Ausgabedateien von SQL-Sitzungen mit mehr Befehlen als nur zwei verallgemeinert werden.


Ich mag das. Ich weiß nicht viel über SQL - aber gibt es sonst keine Chance, dass die Eingabeaufforderungen am Kopf der Ausgabezeilen auftreten?
mikeserv

4

edist 'der Standard-Texteditor' und sollte auf Systemen ohne GNU verfügbar sein sed. Es wurde ursprünglich als Texteditor entwickelt, eignet sich jedoch gut für Skripte.

printf '%s\n' 1d '$d' w q | ed Element_query

1dlöscht die erste Zeile der Datei $d(in Anführungszeichen gesetzt, damit die Shell nicht glaubt, dass es sich um eine Variable handelt), löscht die letzte Zeile, wschreibt die Datei und qbeendet sie ed. printfwird hier verwendet, um die Befehle zu formatieren ed- auf jeden muss eine neue Zeile folgen; es gibt natürlich auch andere möglichkeiten, dies zu erreichen.


3

Es gibt verschiedene Möglichkeiten, führende und nachfolgende Zeilen aus einer Datei zu entfernen.

Sie können awksowohl die Mustererkennung als auch die Zeilenzählung verwenden,

#you need to know length to skip last line, assume we have 100 lines
awk 'NR>1 && NR<100 {print $0};' < inputfile
#or
awk '/SQL/ {next}; {print $0;};' < inputfile |awk 'NR>1&& NR<10 {print $0};'

Sie können grep -vLinien, die Sie nicht möchten, anhand des Musters ausschließen und mithilfe der -EOption mehrere Muster abgleichen.

grep -v -E "SQL>" < inputfile > outputfile

Sie können headund verwenden tail, um bestimmte Zeilenzahlen zu kürzen,

lines=$((`wc -l < inputfile`-2)) #how many lines in file, less 2
head -n-1 < inputfile | head -n$lines > outputfile
#or
tail -n+2 < inputfile | head -n$lines > outputfile

Sie können vi/vimdie erste und letzte Zeile (n) verwenden und löschen.

vi inputfile
:1
dd
:$
dd
:w! outputfile
:x

Sie könnten ein Perl-Skript verwenden, die erste Zeile überspringen, jede Zeile speichern und drucken, wenn Sie eine nächste Zeile erhalten.

#left as exercise for the reader :-)

1
Für die heads braucht man die Pfeife eigentlich nicht, und eigentlich ist es besser, sie gar nicht zu benutzen, wenn man damit durchkommt. Dabei head | headkönnen die beiden Prozesse zwar gleichzeitig ausgeführt werden, sie verarbeiten jedoch praktisch alle Daten redundant. Wenn Sie dies stattdessen tun { head >dump; head >save; } <in, überspringen Sie nur nach Versatz - der erste liest 10 Zeilen aus >dumpund der zweite liest die nächsten 10 Zeilen aus >save.
mikeserv

3

Sie würden bei weitem besser bedient werden Wegschneiden die SQL - Befehle. Sie können dies auf zwei Arten tun:

  1. Wenn Sie absolut sicher , dass die Folge „ SQL>“ ist nicht irgendwo sonst in der Ausgabe auftreten,

    grep -v -F 'SQL> ' < infile > outfile
  2. Wenn Sie nicht so sicher sind,

    grep -v '^SQL> .*;$' < infile > outfile

Die zweite Version ist langsamer, aber genauer: Sie ignoriert Zeilen, die genau mit "SQL>" beginnen und mit einem Semikolon enden. Dies scheint die zu entfernenden Zeilen zu beschreiben.

Es ist jedoch besser, diese zusätzliche Ausgabe zunächst nicht in die Datei zu schreiben. Die meisten SQL-Systeme haben eine Möglichkeit, dies zu tun. Ich bin nicht sehr vertraut mit Oracle, aber vielleicht ist diese Antwort hilfreich.


3

Sie können die Zeilen zwischen einem Bereich in auswählen awk(dies setzt voraus, dass Sie wissen, wie viele Zeilen es gibt):

awk 'NR>1 && NR < 3' file

Oder in Perl:

perl -ne 'print if $.>1 && $.<3' file

Wenn Sie nicht wissen, wie viele Zeilen es gibt, können Sie diese im Handumdrehen mit berechnen grep(beachten Sie, dass dies keine leeren Zeilen zählt, verwenden Sie auch, um sie grep -c '' filezu zählen):

awk -vm="$(grep -c . file2.txt)" 'NR>1 && NR<m' file2.txt

3

Versuchen Sie diese Lösung:

tail -n +2 name_of_file | head -n-1

Anpassung

Sie können es leicht anpassen, um die n ersten Zeilen zu löschen, die das +2von ändern tail;
oder um die letzten n Zeilen zu löschen, die das -1von ändern head.


Diese Lösung ist falsch, da die erste Zeile gedruckt wird.
Xhienne

1
@ xhienne Sorry, es war ein Fehler. Ich habe 1 anstelle von 2 als Parameter für "tail" geschrieben. Jetzt funktioniert es, danke! :)
Gabrer

1

Verwenden von awk:

< inputfile awk 'NR>1 {print r}; {r=$0}' > outputfile
  • < inputfile: leitet den Inhalt von inputfileto awk's umstdin
  • > outputfile: leitet den Inhalt von awk's weiter stdoutzuoutputfile
  • NR>1: führt die folgenden Aktionen nur aus, wenn die Anzahl der verarbeiteten Datensätze größer als 1 ist
  • {print r}: druckt den Inhalt der Variablen r
  • {r=$0}: Weist der Variablen den Inhalt des Datensatzes zu, der gerade verarbeitet wird r

Also bei der ersten Ausführung des awk Skripts wird also der erste Aktionsblock nicht ausgeführt, während der zweite Aktionsblock ausgeführt wird und der Inhalt des Datensatzes der Variablen zugewiesen wird r. Bei der zweiten Ausführung wird der erste Aktionsblock ausgeführt, und der Inhalt der Variablen rwird gedruckt (der vorherige Datensatz wird also gedruckt). Dies hat den Effekt, dass jede verarbeitete Zeile außer der ersten und der letzten gedruckt wird.


Sie schließen die erste Zeile nicht aus. Bei NR == 2 drucken Sie die erste Eingabezeile, die in gespeichert ist r.
Xhienne
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.