Befehl zum Abrufen der n-ten Zeile von STDOUT


210

Gibt es einen Bash-Befehl, mit dem Sie die n-te Zeile von STDOUT abrufen können?

Das heißt, etwas, das dies erfordern würde

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

und so etwas tun

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

Mir ist klar, dass dies eine schlechte Praxis wäre, wenn ich Skripte schreibe, die wiederverwendet werden sollen, ABER wenn ich Tag für Tag mit der Shell arbeite, wäre es für mich nützlich, mein STDOUT so filtern zu können.

Mir ist auch klar, dass dies ein halbtrivialer Befehl zum Schreiben wäre (Puffer STDOUT, Rückgabe einer bestimmten Zeile), aber ich möchte wissen, ob es dafür einen Standard- Shell-Befehl gibt, der verfügbar wäre, ohne dass ich ein Skript ablegen müsste.


5
Ich habe gerade für die Verwendung von Wort magic-command
gestimmt

Antworten:


332

Verwenden sed, nur für Abwechslung:

ls -l | sed -n 2p

Die Verwendung dieser Alternative, die effizienter aussieht, da die Eingabe beim Drucken der erforderlichen Zeile nicht mehr gelesen wird, kann beim Zuführvorgang ein SIGPIPE erzeugen, das wiederum eine unerwünschte Fehlermeldung erzeugen kann:

ls -l | sed -n -e '2{p;q}'

Ich habe das oft genug gesehen, dass ich normalerweise das erste verwende (was sowieso einfacher zu tippen ist), obwohl lses kein Befehl ist, der sich beschwert, wenn es SIGPIPE bekommt.

Für eine Reihe von Zeilen:

ls -l | sed -n 2,4p

Für mehrere Linienbereiche:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'

Was bedeutet das p? wie 2p 3p? In dem Sinne verstehe ich, dass es für 2/3 Zeile ist, aber was ist die Theorie / das Verständnis dahinter?
Leo Ufimtsev

4
@LeoUfimtsev: Dies pist die sedAnweisung, die die Zeilen druckt, die durch den vorangegangenen Zeilenbereich gekennzeichnet sind . So 2,4pbedeutet Druckzeilen 2, 3, 4.
Jonathan Leffler

3
Und nbedeutet, NICHT alle anderen Zeilen zu drucken
Timo

85
ls -l | head -2 | tail -1

13
+2, aber ich würde vorschlagen head -n 2 | tail -n 1- moderne Köpfe und Schwänze neigen dazu, ansonsten Warnungen auszugeben.
Michael Krelin - Hacker

2
Die Verwendung von zwei Prozessen zum Filtern der Daten ist ein wenig übertrieben (aber angesichts der Leistung von Maschinen in diesen Tagen würden sie wahrscheinlich damit fertig werden). Das Verallgemeinern, um einen Bereich zu behandeln, ist nicht ganz trivial (für den Bereich N..M, den Sie verwenden head -n M | tail -n M-N+1), und das Verallgemeinern, um mehrere Bereiche zu behandeln, ist nicht möglich.
Jonathan Leffler

3
Kopf in Schwanz gepaspelt? schrecklich. Mehrere bessere Möglichkeiten, dies zu tun.
Camh

16
Tolle Sache an der * nix-Kommandozeile: Eine Million und eine Möglichkeit, alles zu erledigen, und jeder hat einen Favoriten und hasst alle anderen Möglichkeiten ... :-)
bettelt

1
So drucken Sie einen $ ls -l | awk '{if (NR>=4 && NR<=64) print}'
Zeilenbereich auf

41

Alternative zum schönen Kopf / Schwanz-Weg:

ls -al | awk 'NR==2'

oder

ls -al | sed -n '2p'

1
mein awk ist besser, aber sed ist nett.
Michael Krelin - Hacker

Keine Notwendigkeit für ein "Wenn" - siehe die Antwort des Hackers (mit Ausnahme des Teils über das Finden jeder Datei auf dem System). ;-)
Bis auf weiteres angehalten.

Wofür steht das '2p'?
Zerkleinern

1
@shredding kurze Antwort zweite Zeile drucken; long -> Bei Verwendung erhalten nur Zeilen, die dem Muster entsprechen, den Befehl nach der Adresse. Kurz gesagt, bei Verwendung mit dem Flag / p werden übereinstimmende Zeilen zweimal gedruckt: sed '/ PATTERN / p' Datei. Und natürlich ist MUSTER ein regulärer Ausdruck mehr
Medhat

Was ist, wenn 2es sich um eine Variable handelt? In jedem Beispiel werden einfache Anführungszeichen verwendet, bei einer Variablen benötigen Sie jedoch doppelte Anführungszeichen. Könnten wir awk "programmieren", um auch mit einfachen Anführungszeichen zu arbeiten, wenn wir vars verwenden? Beispiel line =1; ls | awk 'NR==$line'. Ich weiß, dass bashdoppelte Anführungszeichen mit vars verwendet werden.
Timo

21

Von sed1line :

# print line number 52
sed -n '52p'                 # method 1
sed '52!d'                   # method 2
sed '52q;d'                  # method 3, efficient on large files

Von awk1line :

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files

9

Der Vollständigkeit halber ;-)

kürzerer Code

find / | awk NR==3

kürzere Lebensdauer

find / | awk 'NR==3 {print $0; exit}'

Du machst keine Witze über die Vollständigkeit!
Bis auf weiteres angehalten.

Ich bin es nicht ;-) Eigentlich weiß ich nicht, warum jeder es benutzt ls- die ursprüngliche Frage betraf die von jemandem STDOUT, also dachte ich, es ist besser, es größer zu haben.
Michael Krelin - Hacker

Zumindest mit GNU awk ist die Standardaktion { print $0 }, also ist "awk" NR == 3 eine kürzere Möglichkeit, dasselbe zu schreiben.
Ephemient

Sie sollten dieser Aktion wahrscheinlich "; exit" hinzufügen. Es macht keinen Sinn, den Rest der Zeilen zu verarbeiten, wenn Sie nichts damit anfangen wollen.
Camh

@camh: Siehe Jonathan Leffers Antwort dazu: "Kann im Fütterungsprozess ein SIGPIPE erzeugen, das wiederum eine unerwünschte Fehlermeldung erzeugen kann."
Ephemient


6

Sie können awk verwenden:

ls -l | awk 'NR==2'

Aktualisieren

Der obige Code erhält aufgrund eines Fehleres nach dem anderen nicht das, was wir wollen: Die erste Zeile des Befehls ls -l ist die Gesamtzeile . Dafür funktioniert der folgende überarbeitete Code:

ls -l | awk 'NR==3'

4

Ist Perl für Sie leicht verfügbar?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

Ersetzen Sie natürlich 7 durch eine beliebige Zahl.


Dies ist mein Favorit, da es am einfachsten zu verallgemeinern scheint, was ich persönlich war. Danach ist jedes n-te perl -n -e 'if ($.% 7 == 0) {print; Ausfahrt (0); } '
mat kelcey

@matkelcey Sie können auch $ awk 'NR % 7' filenamejede 7. Zeile in der Datei drucken.
user10607

3

Ein anderes Poster schlug vor

ls -l | head -2 | tail -1

Wenn Sie jedoch den Kopf in den Schwanz leiten, sieht es so aus, als würde alles bis zur Linie N zweimal verarbeitet.

Schwanz in den Kopf leiten

ls -l | tail -n +2 | head -n1

wäre effizienter?


0

Ja, der effizienteste Weg (wie bereits von Jonathan Leffler ausgeführt) ist die Verwendung von sed mit print & quit:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

Die Verwendung von Pipefail und PIPESTATUS ist sehr interessant und dies trägt viel zu dieser Seite bei.
Mike S

0

Hmm

sed hat in meinem Fall nicht funktioniert. Ich schlage vor:

für "ungerade" Zeilen 1,3,5,7 ... ls | awk '0 == (NR + 1)% 2'

für "gerade" Zeilen 2,4,6,8 ls | awk '0 == (NR)% 2'


-1

Für mehr Vollständigkeit ..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

Werfen Sie die Zeilen weg, bis Sie zur zweiten gelangen, und drucken Sie danach die erste Zeile aus. Es wird also die 3. Zeile gedruckt.

Wenn es nur die zweite Zeile ist ..

ls -l | (read; head -n1)

Setzen Sie so viele Lesungen wie nötig.


-1

Ich habe dies in meinem .bash_profile hinzugefügt

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

Und es funktioniert

gdf <file name>

Ich bin verwirrt - was hat das mit der Frage zu tun?
Jonathan Leffler
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.