sortiere aber behalte die Kopfzeile oben

56

Ich erhalte eine Ausgabe von einem Programm, das zuerst eine Zeile mit einer Reihe von Spaltenüberschriften und dann eine Reihe von Datenzeilen erstellt. Ich möchte verschiedene Spalten dieser Ausgabe ausschneiden und sie nach verschiedenen Spalten sortiert anzeigen. Ohne die Überschriften kann das Ausschneiden und Sortieren auf einfache Weise über die -kOption ausgeführt werden, eine Teilmenge der Spalten sortzusammen mit cutoder awkanzuzeigen. Bei dieser Sortiermethode werden die Spaltenüberschriften jedoch mit den übrigen Ausgabezeilen gemischt. Gibt es eine einfache Möglichkeit, die Überschriften oben zu belassen?

jonderry
quelle
1
Ich bin auf den folgenden Link gestoßen . Allerdings kann ich diese Technik nicht { head -1; sort; }zum Laufen bringen. Es wird immer ein Teil des Texts nach der ersten Zeile gelöscht. Weiß jemand, warum das passiert?
Jonderry
1
Ich vermute, es headliegt daran, dass mehr als eine Zeile in einen Puffer eingelesen und das meiste davon weggeworfen wird. Meine sedIdee hatte das gleiche Problem.
Andy
@jonderry - diese Technik funktioniert nur mit lseekfähigen Eingaben, so dass sie beim Lesen aus einer Pipe nicht funktioniert. Es wird funktionieren, wenn Sie zu einer Datei umleiten >outfileund dann ausführen{ head -n 1; sort; } <outfile
don_crissti

Antworten:

58

Die Idee von Andy stehlen und es zu einer Funktion machen, damit es einfacher zu bedienen ist:

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}

Jetzt kann ich tun:

$ ps -o pid,comm | body sort -k2
  PID COMMAND
24759 bash
31276 bash
31032 less
31177 less
31020 man
31167 man
...

$ ps -o pid,comm | body grep less
  PID COMMAND
31032 less
31177 less
Mikel
quelle
ps -C COMMANDist vielleicht angemessener als grep COMMAND, aber es ist nur ein Beispiel. Sie können auch nicht verwenden, -Cwenn Sie auch eine andere Auswahloption verwendet haben, z -U.
Mikel
Oder sollte es vielleicht genannt werden body? Wie in body sortoder body grep. Gedanken?
Mikel
3
Umbenannt von headerin body, weil Sie die Aktion am Körper ausführen. Hoffentlich macht das mehr Sinn.
Mikel
2
Denken Sie daran, bodyalle nachfolgenden Teilnehmer der Pipeline anzurufen :ps -o pid,comm | body grep less | body sort -k1nr
Bischof
1
@Tim Du kannst einfach schreiben <foo body sort -k2oder body sort -k2 <foo. Nur eine zusätzliche Figur von dem, was Sie wollten.
Mikel
37

Sie können den Header mit bash wie folgt oben belassen:

command | (read -r; printf "%s\n" "$REPLY"; sort)

Oder mach es mit Perl:

command | perl -e 'print scalar (<>); print sort { ... } <>'
Andy
quelle
2
+1 genial. Eine Bündelung als Shell-Funktion lohnt sich meiner Meinung nach.
Mikel
1
+1, aus irgendeinem Grund ist eine Unterschale vorzuziehen, oder ist in Ordnung {}statt ()?
Jonderry
2
IFS=Deaktiviert die Wortteilung beim Lesen der Eingabe. Ich denke nicht, dass es beim Lesen notwendig ist $REPLY. echoerweitert Backslash-Escape, wenn xpg_echogesetzt (nicht die Standardeinstellung); printfist in diesem Fall sicherer. echo $REPLYOhne Anführungszeichen werden Leerzeichen kondensiert. Ich denke, echo "$REPLY"sollte in Ordnung sein. read -rwird benötigt, wenn die Eingabe Backslash-Escape-Zeichen enthalten kann. Einige davon hängen möglicherweise von der Bash-Version ab.
Andy
1
@Andy: Wow, du hast recht, verschiedene Regeln für read REPLY; echo $REPLY(Leerzeichen entfernen ) und read; echo $REPLY(nicht).
Mikel
1
@Andy: IIRC, der Standardwert von xpg_echohängt von Ihrem System ab, z. B. von Solaris. Ich denke , der Standardwert ist true. Deshalb mag Gilles printfso gern: Es ist das einzige, was vorhersehbares Verhalten aufweist.
Mikel
23

Ich habe eine nette awk-Version gefunden , die gut in Skripten funktioniert:

awk 'NR == 1; NR > 1 {print $0 | "sort -n"}'
Michael Kuhn
quelle
1
Ich mag das, aber es bedarf einiger Erklärungen - die Pipe befindet sich innerhalb des awk-Skripts. Wie funktioniert das? Ruft es den sortBefehl extern auf? Kennt jemand mindestens einen Link zu einer Seite, die die Verwendung von Pipes in awk erklärt?
Wildcard
@Wildcard Sie können die offizielle Handbuchseite oder diesen Primer überprüfen .
Lapo
4

Hackisch, aber effektiv: Vor dem Sortieren 0allen Kopfzeilen und 1allen anderen Zeilen voranstellen . Entfernen Sie das erste Zeichen nach dem Sortieren.

… |
awk '{print (NR <= 2 ? "0 " : "1 ") $0}' |
sort -k 1 -k… |
cut -b 3-
Gilles 'SO - hör auf böse zu sein'
quelle
3

Hier sind einige magische Perl-Zeilenrauschen, durch die Sie Ihre Ausgabe leiten können, um alles zu sortieren, aber die erste Zeile ganz oben zu belassen: perl -e 'print scalar <>, sort <>;'

Ryan Thompson
quelle
2

Ich habe die command | {head -1; sort; }Lösung ausprobiert und kann bestätigen, dass sie wirklich alles vermasselt - headliest mehrere Zeilen aus der Pipe ein und gibt dann nur die erste aus. Der Rest der Ausgabe, der head nicht gelesen wurde, wird also sortab Zeile 2 an --NOT den Rest der Ausgabe übergeben!

Das Ergebnis ist, dass Sie Zeilen (und eine Teilzeile!) Am Anfang Ihrer Befehlsausgabe vermissen (außer Sie haben noch die erste Zeile) - eine Tatsache, die leicht durch Hinzufügen einer Pipe wcam Ende von bestätigt werden kann die obige Pipeline - aber das ist außerordentlich schwer zu finden, wenn Sie das nicht wissen! Ich habe mindestens 20 Minuten damit verbracht, herauszufinden, warum ich eine Teilzeile (die ersten 100 Bytes oder so abgeschnitten) in meiner Ausgabe hatte, bevor ich sie löste.

Am Ende tat ich Folgendes, was wunderbar funktionierte und keine zweimalige Ausführung des Befehls erforderte:

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile
sed 1d $myfile | sort

rm $myfile

Wenn Sie die Ausgabe in eine Datei einfügen müssen, können Sie dies folgendermaßen ändern:

myfile=$(mktemp)
whatever command you want to run > $myfile

head -1 $myfile > outputfile
sed 1d $myfile | sort >> outputfile

rm $myfile
Platzhalter
quelle
Sie können das headeingebaute ksh93 oder das lineDienstprogramm (auf Systemen, auf denen noch eines vorhanden ist) oder gnu-sed -u qoder verwenden IFS=read -r line; printf '%s\n' "$line", das die Eingabe byteweise liest, um dies zu vermeiden.
Stéphane Chazelas
1

Ich denke, das ist am einfachsten.

ps -ef | ( head -n 1 ; sort )

oder das, was möglicherweise schneller ist, da es keine Sub-Shell erzeugt

ps -ef | { head -n 1 ; sort ; }

Andere coole Verwendungen

Zeilen nach der Kopfzeile mischen

cat file.txt |  ( head -n 1 ; shuf )

Zeilen nach Kopfzeile umkehren

cat file.txt |  ( head -n 1 ; tac )
user2449151
quelle
2
Siehe unix.stackexchange.com/questions/11856/… . Dies ist eigentlich keine gute Lösung.
Wildcard
1
Funktioniert nicht, cat file | { head -n 1 ; sort ; } > file2nur Showkopf
Peter Krauss
0
command | head -1; command | tail -n +2 | sort
Sarva
quelle
4
Dies beginnt commandzweimal. Daher ist es auf bestimmte Befehle beschränkt. Für den angeforderten psBefehl im Beispiel würde dies jedoch funktionieren.
Jofel
0

Einfach und unkompliziert!

<command> | head -n 1; <command> | sed 1d | sort <....>
  • sed nd ---> 'n' gibt die Zeilennummer an und 'd' steht für delete.
Jatsui
quelle
1
Genau wie Jofel vor anderthalb Jahren zu Sarvas Antwort Stellung genommen hat, beginnt dies commandzweimal. Für den Einsatz in einer Pipeline also nicht wirklich geeignet.
Wildcard
0

Ich kam hierher und suchte nach einer Lösung für den Befehl w. Dieser Befehl zeigt Details darüber an, wer angemeldet ist und was sie tun.

Um die Ergebnisse sortiert anzuzeigen, aber mit den Kopfzeilen oben (es gibt 2 Kopfzeilenzeilen), habe ich Folgendes festgelegt:

w | head -n 2; w | tail -n +3 | sort

Offensichtlich wird der Befehl wzweimal ausgeführt und ist daher möglicherweise nicht für alle Situationen geeignet. Zu seinem Vorteil ist es jedoch wesentlich leichter zu merken.

Beachten Sie, dass die tail -n +3Mittel 'alle Zeilen ab dem 3. anzeigen' (siehe man tailfür Details).

Robert
quelle
-2

Versuchen Sie Folgendes:

wc -l file_name | tail -n $(awk '{print $1-1}') file_name | sort
Barry
quelle
3
Ich verstehe es nicht
Pierre.Vriens