Wie kann ich in tail
der Shell die neueste Datei in einem Verzeichnis erstellen?
linux
shell
shell-script
Itay Moav-Malimovka
quelle
quelle
Antworten:
Analysieren Sie nicht die Ausgabe von ls! Das Parsen der Ausgabe von ls ist schwierig und unzuverlässig .
Wenn Sie dies tun müssen, empfehle ich find. Ursprünglich hatte ich hier nur ein einfaches Beispiel, um Ihnen den Kern der Lösung zu erläutern. Da diese Antwort jedoch recht beliebt zu sein scheint, habe ich mich dazu entschlossen, sie zu überarbeiten, um eine Version bereitzustellen, die sicher kopiert / eingefügt und mit allen Eingaben verwendet werden kann. Sitzen Sie bequem? Wir beginnen mit einem Oneliner, der Ihnen die neueste Datei im aktuellen Verzeichnis liefert:
Nicht gerade ein Oneliner, oder? Hier ist es wieder als Shell-Funktion und zum leichteren Lesen formatiert:
Und nun , dass als oneliner:
Wenn alles andere fehlschlägt, können Sie die oben genannte Funktion in Ihre Liste aufnehmen
.bashrc
und das Problem mit einer Einschränkung als gelöst betrachten. Wenn Sie nur die Arbeit erledigen möchten, müssen Sie nicht weiter lesen.Die Einschränkung dabei ist, dass ein Dateiname, der auf eine oder mehrere Zeilenumbrüche endet, immer noch nicht
tail
korrekt übergeben wird. Die Umgehung dieses Problems ist kompliziert, und ich halte es für ausreichend, dass bei Auftreten eines solchen böswilligen Dateinamens das relativ sichere Verhalten eines "No such file" -Fehlers anstelle eines gefährlicheren auftritt.Saftige Details
Für die Neugierigen ist dies die mühsame Erklärung, wie es funktioniert, warum es sicher ist und warum andere Methoden wahrscheinlich nicht.
Gefahr, Will Robinson
Erstens ist das einzige Byte, das Dateipfade sicher abgrenzen kann, null, da es das einzige Byte ist, das in Dateipfaden auf Unix-Systemen allgemein verboten ist. Wenn Sie eine Liste von Dateipfaden behandeln, ist es wichtig, nur null als Begrenzer zu verwenden und dies auch dann, wenn Sie einen einzelnen Dateipfad von einem Programm zu einem anderen übergeben, auf eine Weise, die keine willkürlichen Bytes beeinträchtigt. Es gibt viele scheinbar richtige Möglichkeiten, um dieses und andere Probleme zu lösen, die fehlschlagen, wenn (auch aus Versehen) angenommen wird, dass in Dateinamen weder neue Zeilen noch Leerzeichen enthalten sind. Keine der beiden Annahmen ist sicher.
Für die heutigen Zwecke besteht der erste Schritt darin, eine durch Nullen getrennte Liste der gefundenen Dateien zu erstellen. Dies ist ziemlich einfach, wenn Sie eine
find
Unterstützung-print0
wie GNUs haben:Diese Liste sagt uns jedoch immer noch nicht, welches das neueste ist, daher müssen wir diese Informationen einbeziehen. Ich verwende den
-printf
Schalter find , mit dem ich festlegen kann, welche Daten in der Ausgabe angezeigt werden. Nicht alle Versionen derfind
Unterstützung-printf
(dies ist kein Standard), GNU find jedoch. Wenn Sie sich ohne befinden, müssen-printf
Sie sich darauf verlassen,-exec stat {} \;
an welchem Punkt Sie alle Hoffnung auf Portabilität aufgeben müssen, die auchstat
nicht Standard ist. Im Moment gehe ich davon aus, dass Sie GNU-Tools haben.Hier frage ich nach dem printf-Format, bei
%T@
dem es sich um die Änderungszeit in Sekunden seit Beginn der Unix-Epoche handelt, gefolgt von einem Punkt und einer Zahl, die Sekundenbruchteile angibt. Ich füge dazu einen weiteren Punkt und dann%p
(das ist der vollständige Pfad zur Datei) hinzu, bevor ich mit einem Null-Byte abschließe.Jetzt habe ich
Es mag selbstverständlich sein, aber aus Gründen der Vollständigkeit wird
-maxdepth 1
verhindert , dassfind
der Inhalt von Unterverzeichnissen\! -type d
aufgelistet wird, und es werden Verzeichnisse übersprungen, die Sie wahrscheinlich nicht möchtentail
. Bisher habe ich Dateien im aktuellen Verzeichnis mit Informationen zum Änderungszeitpunkt, daher muss ich jetzt nach diesem Änderungszeitpunkt sortieren.Es in die richtige Reihenfolge bringen
Standardmäßig werden für
sort
die Eingabe durch Zeilenumbrüche getrennte Datensätze erwartet. Wenn Sie GNU habensort
, können Sie es bitten, durch Null getrennte Datensätze zu erwarten, indem Sie den-z
Schalter verwenden .; für standardsort
gibt es keine lösung. Ich bin nur daran interessiert, nach den ersten beiden Zahlen (Sekunden und Sekundenbruchteilen) zu sortieren, und möchte nicht nach dem tatsächlichen Dateinamen sortieren. Deshalb sage ichsort
zwei Dinge: Erstens sollte der Punkt (.
) ein Feldtrennzeichen sein und zweitens, dass es nur das erste und das zweite Feld verwenden sollte, wenn darüber nachgedacht wird, wie die Datensätze sortiert werden.Zunächst bündele ich drei kurze Optionen, die zusammen keinen Wert haben.
-znr
ist nur eine prägnante Redewendung-z -n -r
). Danach-t .
(das Leerzeichen ist optional) teiltsort
das Feldtrennzeichen mit und-k 1,2
gibt die Feldnummern an: first und second (sort
zählt Felder von eins, nicht von null). Denken Sie daran, dass ein Beispieldatensatz für das aktuelle Verzeichnis wie folgt aussehen würde:Dies bedeutet,
sort
wird zuerst1000000000
und dann0000000000
bei der Bestellung dieses Datensatzes suchen . Die-n
Option gibtsort
an, dass beim Vergleichen dieser Werte ein numerischer Vergleich verwendet werden soll, da beide Werte Zahlen sind. Dies ist möglicherweise nicht wichtig, da die Nummern eine feste Länge haben, aber es schadet nicht.Der andere Schalter
sort
ist-r
für "Rückwärts". Standardmäßig wird bei der Ausgabe einer numerischen Sortierung die niedrigste Zahl zuerst ausgegeben.-r
Sie wird so geändert, dass die niedrigste und die höchste Zahl zuerst aufgelistet werden. Da diese Zahlen Zeitstempel sind, bedeutet dies, dass sie neuer sind und der neueste Datensatz am Anfang der Liste steht.Nur die wichtigen Teile
Da die Liste der Dateipfade
sort
nun die gewünschte Antwort enthält, suchen wir ganz oben. Was bleibt, ist eine Möglichkeit zu finden, die anderen Datensätze zu verwerfen und den Zeitstempel zu entfernen. Leider akzeptieren auch GNUhead
undtail
Switches keine, damit sie mit null-getrennten Eingaben arbeiten. Stattdessen benutze ich eine while-Schleife als eine Art armer Mannhead
.Zuerst habe ich deaktiviert,
IFS
damit die Dateiliste nicht der Wortteilung unterworfen wird. Als nächstes erzähle ichread
zwei Dinge: Interpretiere keine Escape-Sequenzen in der Eingabe (-r
) und die Eingabe wird mit einem Null-Byte (-d
) begrenzt; Hier wird die leere Zeichenfolge''
verwendet, um "kein Trennzeichen", auch durch null getrennt, anzugeben. Jeder Datensatz wird in die Variable eingelesen,record
sodass sie bei jedemwhile
Durchlauf einen einzelnen Zeitstempel und einen einzelnen Dateinamen hat. Beachten Sie, dass dies-d
eine GNU-Erweiterung ist. Wenn Sie nur einen Standardread
haben, funktioniert diese Technik nicht und Sie haben wenig Rückgriff.Wir wissen, dass die
record
Variable aus drei Teilen besteht, die alle durch Punkte getrennt sind. Mit demcut
Dienstprogramm ist es möglich, einen Teil davon zu extrahieren.Hier wird der gesamte Datensatz an
printf
und von dort weitergeleitetcut
; In Bash können Sie dies mit einem Here-String weiter vereinfachen ,cut -d. -3f- <<<"$record"
um eine bessere Leistung zu erzielen. Wir sagencut
zwei Dinge: Erstens-d
sollte dabei ein bestimmtes Trennzeichen zur Identifikation von Feldern (wie beimsort
Trennzeichen.
) verwendet werden. Secondcut
wird angewiesen-f
, nur Werte aus bestimmten Feldern zu drucken. Die Feldliste wird als Bereich3-
angegeben, der den Wert aus dem dritten Feld und aus allen folgenden Feldern angibt. Dies bedeutet, dasscut
alles bis einschließlich der Sekunde.
, die im Datensatz gefunden wird, gelesen und ignoriert wird und dann der Rest gedruckt wird, der der Dateipfadteil ist.Nachdem Sie den neuesten Dateipfad gedruckt haben, müssen Sie nicht mehr weitermachen:
break
Verlassen Sie die Schleife, ohne den zweiten Dateipfad aufzurufen.Das einzige, was übrig bleibt, ist die Ausführung
tail
auf dem von dieser Pipeline zurückgegebenen Dateipfad. In meinem Beispiel haben Sie vielleicht bemerkt, dass ich dies getan habe, indem ich die Pipeline in eine Subshell eingeschlossen habe. Was Sie vielleicht nicht bemerkt haben, ist, dass ich die Subshell in doppelte Anführungszeichen gesetzt habe. Dies ist wichtig, da eine nicht zitierte Subshell-Erweiterung trotz all dieser Bemühungen, für alle Dateinamen sicher zu sein, letztendlich immer noch zu Problemen führen kann. Eine ausführlichere Erklärung finden Sie bei Interesse. Der zweite wichtige, aber leicht zu übersehende Aspekt beim Aufruf vontail
ist, dass ich ihm die Option gegeben habe--
, bevor ich den Dateinamen erweitert habe. Dies wird anweisentail
dass keine weiteren Optionen angegeben werden und alles, was folgt, ein Dateiname ist, wodurch es sicher ist, Dateinamen zu behandeln, die mit beginnen-
.quelle
tail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
head
undtail
Switches keine, damit sie mit null-getrennten Eingaben arbeiten." Mein Ersatz fürhead
:… | grep -zm <number> ""
.Wenn Sie über Dateinamen mit Leerzeichen besorgt sind,
quelle
Sie können verwenden:
Das
$()
Konstrukt startet eine Sub-Shell, die den Befehlls -1t
ausführt (alle Dateien in zeitlicher Reihenfolge, eine pro Zeile) und diese weiterleitet,head -1
um die erste Zeile (Datei) zu erhalten.Die Ausgabe dieses Befehls (die neueste Datei) wird dann
tail
zur Verarbeitung übergeben.Beachten Sie, dass dies das Risiko birgt, ein Verzeichnis zu erhalten, wenn dies der zuletzt erstellte Verzeichniseintrag ist. Ich habe diesen Trick in einem Alias verwendet, um die neueste Protokolldatei (aus einem rotierenden Satz) in einem Verzeichnis zu bearbeiten, das nur diese Protokolldateien enthält.
quelle
-1
ist nicht nötig,ls
macht das für dich, wenn es in einer Pipe ist. Vergleichenls
undls|cat
zum Beispiel.Auf POSIX-Systemen kann der zuletzt erstellte Verzeichniseintrag nicht abgerufen werden. Jeder Verzeichniseintrag hat
atime
,mtime
undctime
im Gegensatz zu Microsoft Windows,ctime
nicht CreationTime, sondern "Zeitpunkt der letzten Statusänderung".Das Beste, was Sie bekommen können, ist, "die zuletzt geänderte Datei zu beschneiden", was in den anderen Antworten erklärt wird. Ich würde für diesen Befehl gehen:
Beachten Sie die Anführungszeichen um den
ls
Befehl. Dadurch funktioniert das Snippet mit fast allen Dateinamen.quelle
Ich möchte nur die Dateigrößenänderung sehen, die Sie beobachten können.
quelle
In
zsh
:Siehe: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers , hier wird
m
die Änderungszeit angegebenm[Mwhms][-|+]n
, und das Vorhergehendeo
bedeutet, dass die Sortierung auf die eine Weise erfolgt (O
Sortierung auf die andere Weise). Das.
bedeutet nur normale Dateien. In den Klammern wird[1]
der erste Eintrag ausgewählt. Drei verwenden[1,3]
, um die älteste Verwendung zu erhalten[-1]
.Es ist schön kurz und wird nicht verwendet
ls
.quelle
Es gibt wahrscheinlich eine Million Möglichkeiten, dies zu tun, aber ich würde es folgendermaßen tun:
Die Bits zwischen den Backticks (die zitatähnlichen Zeichen) werden interpretiert und das Ergebnis an tail zurückgegeben.
quelle
Eine einfache:
funktioniert gut für mich.
Das Problem besteht darin, Dateien abzurufen, die generiert werden, nachdem Sie den Befehl tail gestartet haben. Wenn Sie dies jedoch nicht benötigen (da sich alle oben genannten Lösungen nicht darum kümmern), ist das Sternchen nur eine einfachere Lösung, IMO.
quelle
quelle
Jemand hat es gepostet und dann aus irgendeinem Grund gelöscht, aber dies ist die einzige, die funktioniert, also ...
quelle
ls
nicht die klügste Sache ist ...Erläuterung:
quelle
quelle