Holen Sie sich die neueste Datei in einem Verzeichnis unter Linux


181

Suchen Sie nach einem Befehl, der die letzte einzelne Datei in einem Verzeichnis zurückgibt.

Kein Grenzwertparameter für ls...


1
watch -n1 'ls -Art | tail -n 1'- zeigt die allerletzten Dateien

Die meisten Antworten hier analysieren die Ausgabe lsoder Verwendung, findohne -print0die es problematisch ist, mit nervigen Dateinamen umzugehen. Immer nützlich zu erwähnen: BashFAQ099, das eine POSIX-Antwort auf dieses Problem gibt
kvantour

Antworten:


263
ls -Art | tail -n 1

Nicht sehr elegant, aber es funktioniert.


2
Mit ls -Artls können Sie auch das Dateidatum anzeigen.
Josir

13
Ein kleines Problem: Diese Version leitet die gesamte Ausgabe von lsan tailweiter und druckt dann nur die letzte Zeile. IMHO ist es besser, in aufsteigender Reihenfolge zu sortieren und headstattdessen zu verwenden, wie chaosvorgeschlagen. Nach dem Drucken wird der Kopf der ersten Zeile beendet. Wenn Sie also die nächste Zeile (tatsächlich den nächsten Block) senden, lswird ein SIGPIPE ausgelöst und auch beendet.
TrueY

1
@TrueY Ich stimme ganz zu. Die Antwort von Chaos ist besser für die Effizienz.
dmckee --- Ex-Moderator Kätzchen

2
Beachten Sie, dass diese Lösung sowohl Verzeichnisse als auch Dateien enthält. Dies könnte eine gute oder eine schlechte Sache sein; es kommt darauf an, was der einzelne Benutzer will. Der Grund, warum ich dies anspreche, ist, dass die ursprüngliche Frage "Datei" lautet.
Sildoreth

2
Ich denke, dass der beabsichtigte Vorteil der Verwendung von Schwanz anstelle von Kopf darin bestehen kann, die Aufnahme der Zeile auszuschließen, die die vom ls zurückgegebene Gesamtsumme beschreibt.
Peter Scott

155
ls -t | head -n1

Dieser Befehl gibt tatsächlich die zuletzt geänderte Datei im aktuellen Arbeitsverzeichnis an.


Entspricht meiner und ist möglicherweise auch effizienter.
dmckee --- Ex-Moderator Kätzchen

funktioniert bei mir nicht ganz, da ich zuerst einige Informationen in meinem ls wiederhole, aber ich bekomme die Verwendung, danke!
Bestätigen Sie den

4
@Josir Dies kann geändert werden, um den Datenstempel mit einzuschließen ls -tl | head -n 1, und es muss nicht die gesamte Tabelle wie bei mir durch die Pipe geschoben werden .
dmckee --- Ex-Moderator Kätzchen

1
@ noɥʇʎԀʎzɐɹƆls -t *.png | head -n1
Chaos

4
Dies gibt einen Ordner zurück, wenn es sich um ls -Art | head -n1die zuletzt geänderte Datei handelt. Verwenden Sie: Wenn Sie speziell die zuletzt geänderte Datei
möchten

79

Dies ist eine rekursive Version (dh sie findet die zuletzt aktualisierte Datei in einem bestimmten Verzeichnis oder einem ihrer Unterverzeichnisse).

find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1

Bearbeiten: Verwenden Sie -f 2-anstelle der -f 2von Kevin vorgeschlagenen


5
Hier ist eine Lösung für Mac:find $DIR -type f -exec stat -lt "%Y-%m-%d" {} \+ | cut -d' ' -f6- | sort -n | tail -1
Benutzer

2
Problem ist, dass der Befehl cut Pfade mit Leerzeichen anstelle von -f 2 abschneidet. Verwenden Sie -f2-, um die Felder 2 an das Zeilenende zurückzugeben
Kevin

2
Wie im Vorschlag von Chaos können Sie, wenn Sie die Sortierung mithilfe von umkehren sort -nr, head -n 1anstelle von verwenden tail -n 1und die Effizienz geringfügig verbessern. (Obwohl, wenn Sie einen rekursiven Fund sortieren, das Erhalten der ersten oder letzten Zeile nicht der langsame Teil ist.)
destenson

2
Sehr hilfreich! Ich finde es freundlicher, wenn ich zu ls geleitet werde:find ./ -type f -printf "%T@ %p\n" -ls | sort -n | cut -d' ' -f 2- | tail -n 1 | xargs -r ls -lah
Simon

Die Mac-Version von @ user sortiert / zeigt nur das Datum an, nicht die Uhrzeit. Hier ist eine feste Version:find $DIR -type f -exec stat -lt "%F %T" {} \+ | cut -d' ' -f6- | sort -n | tail -1
Yoz

11

Ein Hinweis zur Zuverlässigkeit:

Da das Zeilenumbruchzeichen genauso gültig ist wie jedes in einem Dateinamen, ist jede Lösung, die auf Zeilen wie den head/ tailbasiert basiert, fehlerhaft.

Bei GNU lsbesteht eine weitere Option darin, die --quoting-style=shell-alwaysOption und ein bashArray zu verwenden:

eval "files=($(ls -t --quoting-style=shell-always))"
((${#files[@]} > 0)) && printf '%s\n' "${files[0]}"

(Fügen Sie die -AOption hinzu, lswenn Sie auch versteckte Dateien berücksichtigen möchten.)

Wenn Sie sich auf reguläre Dateien beschränken möchten (Verzeichnisse, Fifos, Geräte, Symlinks, Sockets ... ignorieren), müssen Sie auf GNU zurückgreifen find.

Mit Bash 4.4 oder neuer (für readarray -d) und GNU Coreutils 8.25 oder neuer (für cut -z):

readarray -t -d '' files < <(
  LC_ALL=C find . -maxdepth 1 -type f ! -name '.*' -printf '%T@/%f\0' |
  sort -rzn | cut -zd/ -f2)

((${#files[@]} > 0)) && printf '%s\n' "${files[0]}"

Oder rekursiv:

readarray -t -d '' files < <(
  LC_ALL=C find . -name . -o -name '.*' -prune -o -type f -printf '%T@%p\0' |
  sort -rzn | cut -zd/ -f2-)

Am besten wäre es, die zshGlob-Qualifikationsmerkmale zu verwenden, anstatt bashall diesen Ärger zu vermeiden:

Neueste reguläre Datei im aktuellen Verzeichnis:

printf '%s\n' *(.om[1])

Einschließlich versteckter:

printf '%s\n' *(D.om[1])

Zweitneueste:

printf '%s\n' *(.om[2])

Überprüfen Sie das Alter der Datei nach der Symlink-Auflösung:

printf '%s\n' *(-.om[1])

Rekursiv:

printf '%s\n' **/*(.om[1])

Wenn das Vervollständigungssystem ( compinitund Co) aktiviert ist, Ctrl+Xmwird es zu einem Vervollständiger, der auf die neueste Datei erweitert wird.

So:

vi Ctrl+Xm

Sie würden die neueste Datei bearbeiten (Sie können auch sehen, welche sie ist, bevor Sie drücken Return).

vi Alt+2Ctrl+Xm

Für die zweitneueste Datei.

vi * .cCtrl+Xm

für die neueste cDatei.

vi * (.)Ctrl+Xm

für die neueste reguläre Datei (weder Verzeichnis noch FIFO / Gerät ...) und so weiter.


Danke für die zshTipps. Könnten Sie einen Link zu weiteren Details in zsh-Dokumenten bereitstellen?
am

@freezed, sieheinfo zsh qualifiers
Stephane Chazelas

Danke @Stephane Chazelas
am

9

Ich benutze:

ls -ABrt1 --group-directories-first | tail -n1

Es gibt mir nur den Dateinamen, ausgenommen Ordner.


es sei denn, ein Verzeichnis hat nur Verzeichnisse
ealfonso

8

ls -lAtr | tail -1

Die anderen Lösungen enthalten keine Dateien, die mit beginnen '.' .

Dieser Befehl enthält auch '.'und '..', was möglicherweise das ist, was Sie wollen oder nicht:

ls -latr | tail -1


Wird dieser zurückkehren "." Wurde das Verzeichnis kürzlich geändert als eine Datei (z. B. durch Löschen)? Vielleicht ist das das gewünschte Verhalten, vielleicht auch nicht. Aber du hast so oder so recht.
dmckee --- Ex-Moderator Kätzchen

6

Kurzgeschlossene Variante basierend auf der Antwort von dmckee:

ls -t | head -1

Die -1Syntax für head ist jedoch schon vor einiger Zeit veraltet und -n 1sollte verwendet werden.
Tink

5

Ich mag echo *(om[1])( zshSyntax), da dies nur den Dateinamen angibt und keinen anderen Befehl aufruft.


1
Funktioniert gut, wenn Sie zsh verwenden. Verwenden Sie nicht zsh? Ich denke, Bash ist die Standardeinstellung in Ubuntu. Sie können zsh installieren oder einen entsprechenden Befehl für bash finden.
MikeB

3
Der Befehl lautet "echo"; Es wertet nur seine Parameter aus und sendet sie an die Standardausgabe. In diesem Fall hat das Glob-Qualifikationsmerkmal "om [1]" "o", was bedeutet, dass die übereinstimmenden Dateien nach "m" sortiert werden, was der Änderungszeit entspricht. Und von dieser geordneten Liste nehmen Sie [1], die erste Datei. Informationen zu globalen
Qualifiers MikeB

4

Die Such- / Sortierlösung funktioniert hervorragend, bis die Anzahl der Dateien wirklich groß wird (wie bei einem gesamten Dateisystem). Verwenden Sie stattdessen awk, um nur die neueste Datei zu verfolgen:

find $DIR -type f -printf "%T@ %p\n" | 
awk '
BEGIN { recent = 0; file = "" }
{
if ($1 > recent)
   {
   recent = $1;
   file = $0;
   }
}
END { print file; }' |
sed 's/^[0-9]*\.[0-9]* //'

3

Ich persönlich bevorzuge es, so wenige nicht eingebaute bashBefehle wie möglich zu verwenden (um die Anzahl der teuren Fork- und Exec-Systemaufrufe zu reduzieren). Um nach Datum zu sortieren ls, musste das aufgerufen werden. Aber die Verwendung von headist nicht wirklich notwendig. Ich verwende den folgenden Einzeiler (funktioniert nur auf Systemen, die Namensrohre unterstützen):

read newest < <(ls -t *.log)

oder um den Namen der ältesten Datei zu erhalten

read oldest < <(ls -rt *.log)

(Beachten Sie den Abstand zwischen den beiden '<' Markierungen!)

Wenn die versteckten Dateien auch benötigt werden, könnte ein Argument hinzugefügt werden.

Ich hoffe das könnte helfen.


Sie sollten erklären, dass der Dateiname in $ älteste / neueste Variable gespeichert ist. Aber +1 für die geringste Anzahl von Gabeln.
Quadrat

@squareatom Ich dachte, lesen ist ziemlich gut bekannt eingebaut. Aber du hast recht, vielleicht auch nicht. Vielen Dank für Ihren Kommentar!
TrueY

3
Angenommen, Sie wissen, was ein eingebautes Gerät ist, dann haben Sie wahrscheinlich Recht. Aber ich habe nur über die Frage des OP nachgedacht. Sie baten um einen Befehl, der etwas zurückgeben würde. Technisch gesehen gibt Ihre Lösung nichts aus. Also füge einfach "&& echo $ latest" oder ähnliches zum Ende hinzu ;-)
squareatom

3

Wenn Sie die rekursive Option R verwenden, können Sie dies hier als Verbesserung für gute Antworten betrachten

ls -arRtlh | tail -50

2
ls -t -1 | sed '1q'

Zeigt das zuletzt geänderte Element im Ordner an. Koppeln Sie mit grep, um die neuesten Einträge mit Schlüsselwörtern zu finden

ls -t -1 | grep foo | sed '1q'

Was ist die '1q'? 1ist für die erste Zeile gleichbedeutend mit head -n 1q?
HattrickNZ

2

Rekursiv:

find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head

1

Versuchen Sie diesen einfachen Befehl

ls -ltq  <path>  | head -n 1

Wenn Sie möchten, dass der Dateiname zuletzt geändert wird, geben Sie path = /ab/cd/*.log ein

Wenn Sie möchten, dass der Verzeichnisname - zuletzt geändert - Pfad = / ab / cd / * /


1

Vorausgesetzt, Sie interessieren sich nicht für versteckte Dateien, die mit a beginnen .

ls -rt | tail -n 1

Andernfalls

ls -Art | tail -n 1

1

Mit nur Bash-Builds, genau nach BashFAQ / 003 :

shopt -s nullglob

for f in * .*; do
    [[ -d $f ]] && continue
    [[ $f -nt $latest ]] && latest=$f
done

printf '%s\n' "$latest"

0

Alle diese ls / tail-Lösungen funktionieren einwandfrei für Dateien in a Verzeichnis - ohne Unterverzeichnisse.

Um alle Dateien (rekursiv) in Ihre Suche einzubeziehen, kann find verwendet werden. gioele schlug vor, die formatierte Suchausgabe zu sortieren. Aber seien Sie vorsichtig mit Leerzeichen (sein Vorschlag funktioniert nicht mit Leerzeichen).

Dies sollte mit allen Dateinamen funktionieren:

find $DIR -type f -printf "%T@ %p\n" | sort -n | sed -r 's/^[0-9.]+\s+//' | tail -n 1 | xargs -I{} ls -l "{}"

Dies sortiert nach Zeit, siehe Mann finden:

%Ak    File's  last  access  time in the format specified by k, which is either `@' or a directive for the C `strftime' function.  The possible values for k are listed below; some of them might not be available on all systems, due to differences in `strftime' between systems.
       @      seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
%Ck    File's last status change time in the format specified by k, which is the same as for %A.
%Tk    File's last modification time in the format specified by k, which is the same as for %A.

So ersetzen Sie einfach %Tmit %Czu sortieren nach Ctime.


Sie haben Recht mit dem Leerraumproblem mit dem Vorschlag von @gioele, müssen jedoch nur einen Bindestrich zur Option -f hinzufügen, um einen Bereich zu erstellen, in dem Folgendes behoben werden kann: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1 Oder behalten Sie alternativ alle außer dem ersten Feld bei: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 1 --complement | tail -n 1
Aaron

0

Suchen der aktuellsten Datei in jedem Verzeichnis nach einem Muster, z. B. der Unterverzeichnisse des Arbeitsverzeichnisses, deren Name mit "tmp" endet (Groß- und Kleinschreibung wird nicht berücksichtigt):

find . -iname \*tmp -type d -exec sh -c "ls -lArt {} | tail -n 1" \;

0
ls -Frt | grep "[^/]$" | tail -n 1

1
Es gibt andere Antworten, die die Frage des OP stellen, und sie wurden vor vielen Jahren veröffentlicht. Stellen Sie beim Posten einer Antwort sicher, dass Sie entweder eine neue Lösung oder eine wesentlich bessere Erklärung hinzufügen, insbesondere wenn Sie ältere Fragen beantworten.
help-info.de

2
@ help-info.de Bitte stimmen Sie nicht ab, um Antworten zu löschen, die nur Code enthalten. Sie mögen nicht großartig sein, aber das Löschen ist für Dinge, die keine Antworten sind
Machavity

@Machavity - Ich hoffe nicht gescheitert zu sein und habe nur einen Kommentar für die späte Antwort abgegeben.
help-info.de

0

Wenn Sie die zuletzt geänderte Datei einschließlich aller Unterverzeichnisse erhalten möchten, können Sie dies mit diesem kleinen Oneliner tun:

find . -type f -exec stat -c '%Y %n' {} \; | sort -nr | awk -v var="1" 'NR==1,NR==var {print $0}' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done

Wenn Sie dasselbe nicht für geänderte Dateien tun möchten, sondern für Dateien, auf die zugegriffen wird, müssen Sie einfach die ändern

% Y Parameter aus dem STAT - Befehl % X . Und Ihr Befehl für die zuletzt aufgerufenen Dateien sieht folgendermaßen aus:

find . -type f -exec stat -c '%X %n' {} \; | sort -nr | awk -v var="1" 'NR==1,NR==var {print $0}' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done

Für beide Befehle können Sie auch den Parameter var = "1" ändern, wenn Sie mehr als nur eine Datei auflisten möchten.


-1

Ich musste es auch tun und fand diese Befehle. diese arbeiten für mich:

Wenn Sie die letzte Datei nach dem Erstellungsdatum im Ordner (Zugriffszeit) haben möchten:

ls -Aru | tail -n 1  

Und wenn Sie die letzte Datei möchten, deren Inhalt sich ändert (Zeit ändern):

ls -Art | tail -n 1  
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.