Obtenir le fichier le plus récent dans un répertoire sous Linux


183

Recherche d'une commande qui renverra le fichier le plus récent dans un répertoire.

Ne pas voir un paramètre de limite à ls...


1
watch -n1 'ls -Art | tail -n 1'- affiche les tout derniers fichiers

La plupart des réponses ici analysent la sortie lsou l'utilisation findsans -print0laquelle est problématique pour gérer les noms de fichiers ennuyeux. Toujours utile de mentionner: BashFAQ099 qui donne une réponse POSIX à ce problème
kvantour

Réponses:


266
ls -Art | tail -n 1

Pas très élégant, mais ça marche.


2
en utilisant ls -Artls, vous pouvez également afficher la date du fichier.
Josir

13
Un problème mineur: cette version redirige toute la sortie de lsvers tail, puis n'imprime que la dernière ligne. À mon humble avis, il est préférable de trier par ordre croissant et d'utiliser à la headplace, comme chaossuggéré. Après l'impression, l'en-tête de la première ligne se ferme, donc l'envoi de la ligne suivante (en fait le bloc suivant) augmentera un SIGPIPE et se lsfermera également.
TrueY

1
@TrueY Je suis tout à fait d'accord. La réponse du chaos est meilleure pour l'efficacité.
dmckee --- ex-moderator chaton

2
Notez que cette solution comprend des répertoires ainsi que des fichiers. Cela peut être une bonne ou une mauvaise chose; cela dépend de ce que veut l'utilisateur individuel. La raison pour laquelle j'évoque ce point est que la question initiale dit «fichier».
Sildoreth

2
Je pense que le bénéfice attendu de l'utilisation de la queue au lieu de la tête peut être d'exclure l'inclusion de la ligne décrivant le total retourné par les ls.
Peter Scott

155
ls -t | head -n1

Cette commande donne en fait le dernier fichier modifié dans le répertoire de travail actuel.


Équivalent au mien, et peut-être plus efficace aussi.
dmckee --- ex-moderator chaton

ne fonctionne pas tout à fait pour moi car je fais d'abord écho à certaines informations dans mon ls, mais je reçois l'utilisation, merci!
Ack

4
@Josir Cela peut être modifié pour inclure l'horodatage avec ls -tl | head -n 1, et il n'est pas nécessaire de pousser toute la table à travers le tube comme le fait le mien.
dmckee --- ex-moderator chaton

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

4
Cela renvoie un dossier s'il s'agit de la dernière modification, utilisez: ls -Art | head -n1si vous voulez spécifiquement le dernier fichier modifié
achasinh

79

Il s'agit d'une version récursive (c'est-à-dire qu'elle trouve le fichier le plus récemment mis à jour dans un certain répertoire ou dans l'un de ses sous-répertoires)

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

Edit: utilisez -f 2-plutôt -f 2que comme suggéré par Kevin


5
Voici une solution pour Mac:find $DIR -type f -exec stat -lt "%Y-%m-%d" {} \+ | cut -d' ' -f6- | sort -n | tail -1
utilisateur

2
Le problème est que la commande cut tronque les chemins avec des espaces au lieu de -f 2 utilisez -f2- pour renvoyer les champs 2 à la fin de la ligne
Kevin

2
Comme dans la suggestion du chaos, si vous inversez le tri en utilisant sort -nr, vous pouvez utiliser à la head -n 1place de tail -n 1et améliorer légèrement l'efficacité. (Bien que si vous triez une recherche récursive, obtenir la première ou la dernière ligne ne sera pas la partie la plus lente.)
destenson

2
Très utile! Je le trouve plus sympathique s'il est canalisé à ls cependant:find ./ -type f -printf "%T@ %p\n" -ls | sort -n | cut -d' ' -f 2- | tail -n 1 | xargs -r ls -lah
Simon

La version Mac de @ utilisateur trie / affiche uniquement la date, pas l'heure. Voici une version fixe:find $DIR -type f -exec stat -lt "%F %T" {} \+ | cut -d' ' -f6- | sort -n | tail -1
yoz

12

Une note sur la fiabilité:

Puisque le caractère de nouvelle ligne est aussi valide que n'importe quel autre dans un nom de fichier, toute solution qui repose sur des lignes comme celles basées sur head/ tailest imparfaite.

Avec GNU ls, une autre option consiste à utiliser l' --quoting-style=shell-alwaysoption et un bashtableau:

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

(ajoutez l' -Aoption à lssi vous souhaitez également prendre en compte les fichiers cachés).

Si vous voulez vous limiter aux fichiers normaux (ne pas tenir compte des répertoires, des fifos, des périphériques, des liens symboliques, des sockets ...), vous devez recourir à GNU find.

Avec bash 4.4 ou plus récent (pour readarray -d) et GNU coreutils 8.25 ou plus récent (pour 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]}"

Ou récursivement:

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

Le mieux ici serait d'utiliser zshet ses qualificatifs glob au lieu d' bashéviter tous ces tracas:

Le fichier régulier le plus récent dans le répertoire courant:

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

Y compris les cachés:

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

Deuxième plus récent:

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

Vérifiez l'âge du fichier après la résolution du lien symbolique:

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

Récursivement:

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

En outre, avec le système de complétion ( compinitet co) activé, Ctrl+Xmdevient un complément qui se développe au fichier le plus récent.

Alors:

vi Ctrl+Xm

Vous ferait éditer le fichier le plus récent (vous aurez également une chance de voir lequel il avant d'appuyer sur Return).

vi Alt+2Ctrl+Xm

Pour le deuxième fichier le plus récent.

vi * .cCtrl+Xm

pour le cfichier le plus récent .

vi * (.)Ctrl+Xm

pour le fichier régulier le plus récent (pas de répertoire, ni de fifo / périphérique ...), et ainsi de suite.


Merci pour les zshconseils. Pourriez-vous fournir un lien vers plus de détails à ce sujet dans la documentation zsh?
gelé le

@freezed, voirinfo zsh qualifiers
Stephane Chazelas

Merci @Stephane Chazelas
gelé le

9

J'utilise:

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

Il me donne juste le nom du fichier, à l'exclusion des dossiers.


sauf si un répertoire n'a que des répertoires
ealfonso

8

ls -lAtr | tail -1

Les autres solutions n'incluent pas les fichiers commençant par '.'.

Cette commande comprendra également '.'et '..', qui peut ou non être ce que vous voulez:

ls -latr | tail -1


Est-ce que celui-ci reviendra "." si le répertoire a été modifié plus récemment que n'importe quel fichier contenu (disons par une suppression)? C'est peut-être le comportement souhaité, peut-être pas. Mais vous avez raison de toute façon.
dmckee --- ex-moderator chaton

6

Variante raccourcie basée sur la réponse de dmckee:

ls -t | head -1

mais la -1syntaxe en tête a été obsolète il y a un certain temps et -n 1devrait être utilisée.
tink

5

J'aime echo *(om[1])(la zshsyntaxe) car cela donne simplement le nom du fichier et n'invoque aucune autre commande.


1
Fonctionne bien si vous utilisez zsh. N'utilisez-vous pas zsh? Je pense que bash est la valeur par défaut dans Ubuntu; vous pouvez installer zsh, ou vous pouvez trouver une commande équivalente pour bash.
MikeB

3
La commande est "echo"; il évalue simplement ses paramètres et les envoie à la sortie standard. Dans ce cas, le qualificatif global "om [1]" a "o", ce qui signifie ordonner les fichiers correspondants par "m", qui est le temps de modification. Et à partir de cette liste ordonnée, prenez [1], qui est le premier fichier. Vous pouvez en savoir plus sur les qualificatifs glob: zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers
MikeB

4

La solution de recherche / tri fonctionne très bien jusqu'à ce que le nombre de fichiers devienne vraiment important (comme un système de fichiers entier). Utilisez plutôt awk pour garder une trace du fichier le plus récent:

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

bashPersonnellement, je préfère utiliser le moins de commandes non intégrées que possible (pour réduire le nombre d'appels système coûteux de fork et exec). Pour trier par date le lsbesoin d'être appelé. Mais l'utilisation de headn'est pas vraiment nécessaire. J'utilise le one-liner suivant (fonctionne uniquement sur les systèmes prenant en charge les tuyaux de nom):

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

ou pour obtenir le nom du fichier le plus ancien

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

(Attention à l'espace entre les deux marques '<'!)

Si les fichiers cachés sont également nécessaires, un argument peut être ajouté.

J'espère que cela pourrait aider.


Vous devez expliquer que le nom de fichier est stocké dans la variable $ la plus ancienne / la plus récente. Mais +1 pour le moins de fourches.
squareatom

@squareatom Je pensais que lire est assez bien connu intégré. Mais vous avez raison, peut-être pas. Merci pour votre commentaire!
TrueY

3
En supposant que vous savez ce qu'est un intégré, vous avez probablement raison. Mais, je pensais juste à la question du PO. Ils ont demandé une commande qui renverrait quelque chose. Techniquement, votre solution ne produit rien. Il suffit donc d'ajouter "&& echo $ plus récent" ou similaire à la fin ;-)
squareatom

3

en utilisant l'option récursive R .. vous pouvez considérer cela comme une amélioration pour de bonnes réponses ici

ls -arRtlh | tail -50

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

Affiche le dernier élément modifié dans le dossier. Associer avec greppour trouver les dernières entrées avec des mots-clés

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

qu'est-ce que le «1q»? 1est pour la première ligne équivaut à head -n 1ce qu'est q?
HattrickNZ

2

Récursivement:

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

1

essayez cette simple commande

ls -ltq  <path>  | head -n 1

Si vous voulez un nom de fichier - dernière modification, chemin = /ab/cd/*.log

Si vous voulez le nom du répertoire - dernière modification, chemin = / ab / cd / * /


1

En supposant que vous ne vous souciez pas des fichiers cachés commençant par un .

ls -rt | tail -n 1

Autrement

ls -Art | tail -n 1

1

Avec uniquement des fonctions intégrées Bash, suivant de près BashFAQ / 003 :

shopt -s nullglob

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

printf '%s\n' "$latest"

0

Toutes ces solutions ls / tail fonctionnent parfaitement bien pour les fichiers dans un répertoire - en ignorant les sous-répertoires.

Afin d'inclure tous les fichiers dans votre recherche (récursivement), find peut être utilisé. gioele a suggéré de trier la sortie de recherche formatée. Mais soyez prudent avec les espaces (sa suggestion ne fonctionne pas avec les espaces).

Cela devrait fonctionner avec tous les noms de fichiers:

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

Cela trie par mtime, voir man find:

%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.

Donc, remplacez simplement %Tpar %Cpour trier par ctime.


Vous avez raison sur le problème d'espace blanc avec la suggestion @gioele, mais il vous suffit d'ajouter un trait d'union à l'option -f pour en faire une plage pour résoudre ce problème: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1 Ou bien, gardez tout sauf le premier champ: find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 1 --complement | tail -n 1
Aaron

0

Recherche du fichier le plus récent dans chaque répertoire selon un modèle, par exemple les sous-répertoires du répertoire de travail dont le nom se termine par "tmp" (insensible à la casse):

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

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

1
Il y a d'autres réponses qui fournissent la question du PO, et elles ont été publiées il y a de nombreuses années. Lorsque vous publiez une réponse, assurez-vous d'ajouter soit une nouvelle solution, soit une explication nettement meilleure, en particulier lorsque vous répondez à des questions plus anciennes.
help-info.de

2
@ help-info.de Veuillez ne pas voter pour supprimer les réponses qui ne contiennent que du code. Ils ne sont peut-être pas géniaux, mais la suppression concerne les choses qui ne sont pas des réponses
Machavity

@Machavity - J'espère ne pas avoir échoué et n'ai fait un commentaire que pour une réponse tardive.
help-info.de

0

Si vous souhaitez obtenir le fichier modifié le plus récent, y compris tous les sous-répertoires, vous pouvez le faire avec ce petit oneliner:

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

Si vous ne voulez pas faire de même pour les fichiers modifiés, mais pour les fichiers accédés, vous devez simplement changer le

% Y paramètre de la stat commande à X% . Et votre commande pour les fichiers consultés les plus récents ressemble à ceci:

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

Pour les deux commandes, vous pouvez également modifier le paramètre var = "1" si vous souhaitez répertorier plusieurs fichiers.


-1

J'avais besoin de le faire aussi, et j'ai trouvé ces commandes. ceux-ci fonctionnent pour moi:

Si vous voulez le dernier fichier par sa date de création dans le dossier (temps d'accès):

ls -Aru | tail -n 1  

Et si vous voulez le dernier fichier dont le contenu a changé (heure de modification):

ls -Art | tail -n 1  
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.