Das ist also für Hausaufgaben, aber ich werde nicht die spezifische Hausaufgabenfrage stellen.
Ich muss Kopf und Schwanz verwenden, um verschiedene Liniensätze aus einer Datei zu ziehen. Also wie die Zeilen 6-11 und 19-24 und speichern Sie beide in einer anderen Datei. Ich weiß, dass ich dies mit Anhängen wie tun kann
head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1.
Aber ich denke nicht, dass wir das sollen.
Gibt es eine bestimmte Möglichkeit, die Head- und Tail-Befehle zu kombinieren und dann in der Datei zu speichern?
head
undtail
? Wenn ja, ist Ihre Lösung so ziemlich das Beste, was Sie tun können. Wenn Sie andere Programme verwenden dürfensed
oderawk
möglicherweise bessere Lösungen zulassen (dh mit weniger Prozessaufrufen).>>
) umgehen, indem Sie die beiden Befehle in Klammern setzen, um ihre verkettete Ausgabe umzuleiten :(head -11 file | tail -6; head -24 file | tail -6) > file1
. Es kommt wirklich auf die persönlichen Vorlieben an, was schöner ist.Antworten:
Sie können dies mit
head
alleiniger und grundlegender Arithmetik tun , wenn Sie Befehle mit{ ... ; }
einem Konstrukt wie gruppierenwo alle Befehle die gleiche Eingabe teilen (danke @mikeserv ).
Das Abrufen der Zeilen 6-11 und 19-24 entspricht:
Im Grunde würden Sie also laufen:
quelle
Mit dem
{ … }
Gruppierungskonstrukt können Sie den Umleitungsoperator auf einen zusammengesetzten Befehl anwenden.Anstatt die ersten M + N Zeilen zu duplizieren und nur die letzten N beizubehalten, können Sie die ersten M Zeilen überspringen und die nächsten N duplizieren. Dies ist bei großen Dateien messbar schneller . Beachten Sie, dass das
+N
Argument vontail
nicht die Anzahl der zu überspringenden Zeilen ist, sondern eins plus das - es ist die Nummer der ersten zu druckenden Zeile mit Zeilen, die von 1 nummeriert sind.In beiden Fällen wird die Ausgabedatei nur einmal geöffnet, die Eingabedatei wird jedoch einmal durchlaufen, damit jedes Snippet extrahiert werden kann. Wie wäre es mit der Gruppierung der Eingaben?
Im Allgemeinen funktioniert dies nicht. (Auf einigen Systemen funktioniert dies möglicherweise, zumindest wenn es sich bei der Eingabe um eine reguläre Datei handelt.) Warum? Wegen der Eingabepufferung . Die meisten Programme, einschließlich
tail
, lesen ihre Eingabe nicht byteweise, sondern jeweils einige Kilobyte, weil sie schneller sind. Sotail
liest ein paar Kilobyte, springt ein wenig am Anfang, geht ein bisschen mehrhead
, und stoppt - aber was gelesen wird , gelesen wird , und ist für den nächsten Befehl nicht zur Verfügung.Ein anderer Ansatz ist die Verwendung von
head
Piped/dev/null
zum Überspringen von Zeilen.Auch dies kann aufgrund der Pufferung nicht garantiert funktionieren. Es funktioniert zufällig mit dem
head
Befehl von GNU coreutils (der auf nicht eingebetteten Linux-Systemen zu finden ist), wenn die Eingabe aus einer regulären Datei stammt. Das liegt daran, dass diese Implementierung von, sobald siehead
gelesen hat, was sie will, die Dateiposition auf das erste Byte setzt, das sie nicht ausgegeben hat. Dies funktioniert nicht, wenn die Eingabe eine Pipe ist.Eine einfachere Möglichkeit, mehrere Zeilenfolgen aus einer Datei zu drucken, besteht darin, ein allgemeineres Tool wie sed oder awk aufzurufen . (Dies kann langsamer sein, ist jedoch nur für extrem große Dateien von Bedeutung.)
quelle
Ich weiß, dass Sie gesagt haben, dass Sie Kopf und Schwanz verwenden müssen, aber sed ist definitiv das einfachere Werkzeug für den Job hier.
Sie können die Blöcke sogar mit einem anderen Prozess in einer Zeichenfolge erstellen und diese durch sed ausführen.
-n negiert die Ausgabe, dann geben Sie Bereiche an, die mit p gedruckt werden sollen, wobei die erste und letzte Nummer des Bereichs durch ein Komma getrennt sind.
Davon abgesehen können Sie entweder die von @don_crissti vorgeschlagene Befehlsgruppierung ausführen oder die Datei einige Male durchlaufen, wobei Kopf / Schwanz bei jedem Durchlauf einen Teil der Zeilen greifen.
Je mehr Zeilen in einer Datei und je mehr Blöcke Sie haben, desto effizienter wird sed.
quelle
Mit
sed
könnten Sie tun:... Möglicherweise könnte eine effizientere Lösung mit gehabt werden
head
. Don hat bereits gezeigt, wie das sehr gut funktionieren könnte, aber ich habe auch damit herumgespielt. Etwas, das Sie tun könnten, um diesen speziellen Fall zu behandeln:... die
head
viermal aufschreiben würdeoutfile
,/dev/null
je nachdem, ob der Wert dieser Iteration$n
eine gerade oder eine ungerade Zahl ist.Für allgemeinere Fälle habe ich dies aus einigen anderen Sachen zusammengeschustert, die ich bereits hatte:
Dies kann dein Ding machen wie:
... was druckt ...
Es erwartet, dass sein erstes Argument eine Wiederholungszahl ist
-
, der ein oder nur ein a vorangestellt ist-
. Wenn eine Zählung angegeben wird, wird das in den folgenden Argumenten angegebene Linienmuster so oft wie angegeben wiederholt und angehalten, sobald dies geschehen ist.Für jedes folgende Argument wird eine negative Ganzzahl interpretiert, um eine Zeilenanzahl anzugeben, in die geschrieben werden soll,
/dev/null
und eine positive Ganzzahl, um eine Zeilenanzahl anzugeben, in die geschrieben werden sollstdout
.Im obigen Beispiel werden also die ersten 5 Zeilen an
/dev/null
, die nächsten 6 anstdout
, die nächsten 7 an/dev/null
und die nächsten 6 erneut an gedrucktstdout
. Nachdem es das letzte seiner Argumente erreicht und die-1
Wiederholungszählung vollständig durchlaufen hat , wird es beendet. Wenn das erste Argument gewesen wäre-2
, hätte es den Vorgang noch einmal wiederholt, oder wenn es-
so lange wie möglich gewesen wäre.Für jeden Arg-Zyklus wird die
while
Schleife einmal durchlaufen. Am Anfang jeder Schleife wird die erste Zeile vonstdin
in die Shell-Variable eingelesen$l
. Dies ist notwendig, dawhile head </dev/null; do :; done
es auf unbestimmte Zeit wiederholt wird -head
zeigt in seiner Rückgabe an, wann das Dateiende erreicht ist. Die Prüfung gegen EOF ist also nur dann gewidmetread
undprintf
wird$l
plus eine neue Zeile geschrieben,stdout
wenn das zweite Argument eine positive ganze Zahl ist.Die
read
Prüfung verkompliziert die Schleife ein wenig, da unmittelbar nach dem Aufruf einer anderen Schleife einefor
Schleife über Argumente iteriert,2-$#
wie in$n
für jede Iteration ihrer übergeordnetenwhile
Schleife dargestellt. Dies bedeutet, dass für jede Iteration das erste Argument von dem in der Befehlszeile angegebenen Wert um eins dekrementiert werden muss, alle anderen jedoch ihre ursprünglichen Werte beibehalten sollten. Daher wird der Wert des$_n
Markers var von jedem subtrahiert, enthält jedoch immer nur a Wert größer als 0 für das erste Argument.Dies stellt die Hauptschleife der Funktion dar, aber der Großteil des Codes befindet sich oben und soll es der Funktion ermöglichen, selbst eine Pipe als Eingabe sauber zu puffern. Dies funktioniert, indem zuerst ein Hintergrundbild
dd
aufgerufen wird, um es bei der Ausgabe in Blockgrößen von 4 KB pro Stück in ein tmpfile zu kopieren. Die Funktion richtet dann eine Halteschleife ein, die fast nie einen einzigen vollständigen Zyklus abschließen sollte, um sicherzustellen, dassdd
mindestens ein einziger Schreibvorgang in die Datei ausgeführt wurde, bevor die Funktion ihren Standard durch einen Dateideskriptor ersetzt, der mit tmpfile und verknüpft ist Danach wird die Verknüpfung der Datei sofort mit aufgehobenrm
. Auf diese Weise kann die Funktion den Stream zuverlässig verarbeiten, ohne dass Traps erforderlich sind oder anderweitig bereinigt werden müssen. Sobald die Funktion den Anspruch auf dem fd freigibt, ist die tmpfile nicht mehr vorhanden, da der einzige benannte Dateisystemlink bereits entfernt wurde.quelle
Verwenden Sie eine Bash-Funktion wie folgt:
Dies ist in diesem Fall ein bisschen übertrieben, aber wenn Ihre Filter größer werden, kann dies zu einem Segen werden.
quelle