En shell, comment puis-je trouver tail
le dernier fichier créé dans un répertoire?
linux
shell
shell-script
Itay Moav -Malimovka
la source
la source
Réponses:
Ne pas analyser la sortie de ls! L'analyse de la sortie de ls est difficile et peu fiable .
Si vous devez le faire, je vous recommande d'utiliser find. À l'origine, j'avais ici un exemple simple simplement pour vous donner l'essentiel de la solution, mais comme cette réponse semble quelque peu populaire, j'ai décidé de la réviser pour fournir une version qui est sûre à copier / coller et à utiliser avec toutes les entrées. Êtes-vous assis confortablement? Nous allons commencer avec un oneliner qui vous donnera le dernier fichier dans le répertoire courant:
Pas tout à fait un oneliner maintenant, n'est-ce pas? Le voici à nouveau comme une fonction shell et formaté pour une lecture plus facile:
Et maintenant que comme oneliner:
Si tout le reste échoue, vous pouvez inclure la fonction ci-dessus dans votre
.bashrc
et considérer le problème résolu, avec une mise en garde. Si vous vouliez simplement faire le travail, vous n'avez pas besoin de lire plus loin.La mise en garde avec ceci est qu'un nom de fichier se terminant par une ou plusieurs nouvelles lignes ne sera toujours pas transmis
tail
correctement. Contourner ce problème est compliqué et je considère suffisant que si un tel nom de fichier malveillant est rencontré, le comportement relativement sûr de rencontrer une erreur "No such file" se produira au lieu de quelque chose de plus dangereux.Détails juteux
Pour les curieux, c'est l'explication fastidieuse de son fonctionnement, pourquoi il est sûr et pourquoi d'autres méthodes ne le sont probablement pas.
Danger, Will Robinson
Tout d'abord, le seul octet qui soit sûr pour délimiter les chemins de fichiers est nul car c'est le seul octet universellement interdit dans les chemins de fichiers sur les systèmes Unix. Il est important lors de la gestion d'une liste de chemins de fichiers de n'utiliser que null comme délimiteur et, lors de la remise d'un seul chemin de fichier d'un programme à un autre, de le faire d'une manière qui ne s'étouffe pas sur des octets arbitraires. Il existe de nombreuses façons apparemment correctes de résoudre ce problème et d'autres qui échouent en supposant (même accidentellement) que les noms de fichiers n'auront ni nouvelles lignes ni espaces. Aucune de ces hypothèses n'est sûre.
Pour les besoins d'aujourd'hui, la première étape consiste à obtenir une liste de fichiers délimités par des valeurs nulles. C'est assez facile si vous avez un
find
support-print0
tel que GNU:Mais cette liste ne nous dit toujours pas laquelle est la plus récente, nous devons donc inclure cette information. Je choisis d'utiliser le
-printf
commutateur find qui me permet de spécifier quelles données apparaissent dans la sortie. Pas toutes les versions defind
support-printf
(ce n'est pas standard) mais GNU find le fait. Si vous vous retrouvez sans,-printf
vous devrez vous fier-exec stat {} \;
à quel point vous devez abandonner tout espoir de portabilité, ce quistat
n'est pas standard non plus. Pour l'instant, je vais continuer en supposant que vous avez des outils GNU.Ici, je demande le format printf
%T@
qui est le temps de modification en secondes depuis le début de l'époque Unix suivi d'un point puis suivi d'un nombre indiquant des fractions de seconde. J'ajoute à cela une autre période, puis%p
(qui est le chemin d'accès complet au fichier) avant de terminer avec un octet nul.Maintenant j'ai
Cela peut aller de soi, mais pour être complet, il
-maxdepth 1
empêchefind
de lister le contenu des sous-répertoires et\! -type d
ignore les répertoires que vous ne voudrez probablement pastail
. Jusqu'à présent, j'ai des fichiers dans le répertoire actuel avec des informations sur l'heure de modification, alors maintenant je dois trier par heure de modification.Le mettre dans le bon ordre
Par défaut,
sort
attend que son entrée soit des enregistrements délimités par des sauts de ligne. Si vous avez GNU,sort
vous pouvez lui demander de s'attendre à des enregistrements séparés par des valeurs nulles à la place en utilisant le-z
commutateur .; pour le standardsort
il n'y a pas de solution. Je ne suis intéressé que par le tri par les deux premiers nombres (secondes et fractions de seconde) et je ne veux pas trier par le nom de fichier réel, donc je dissort
deux choses: d'abord, il doit considérer le point (.
) comme un délimiteur de champ et deuxièmement, il ne doit utiliser les premier et deuxième champs que pour déterminer comment trier les enregistrements.Tout d'abord, je regroupe trois options courtes qui ne prennent aucune valeur ensemble;
-znr
est juste une façon concise de dire-z -n -r
). Après cela-t .
(l'espace est facultatif) indiquesort
le caractère délimiteur de champ et-k 1,2
spécifie les numéros de champ: premier et deuxième (sort
compte les champs à partir de un, pas de zéro). N'oubliez pas qu'un exemple d'enregistrement pour le répertoire actuel ressemblerait à:Cela signifie
sort
que vous regarderez d'abord1000000000
puis0000000000
lors de la commande de ce disque. L'-n
option indiquesort
d'utiliser la comparaison numérique lors de la comparaison de ces valeurs, car les deux valeurs sont des nombres. Cela peut ne pas être important car les nombres sont de longueur fixe mais cela ne fait pas de mal.L'autre commutateur donné
sort
est-r
pour "inverser". Par défaut, la sortie d'un tri numérique sera en premier les nombres les plus bas, la-r
modifie pour qu'elle répertorie les derniers nombres en bas et les premiers en premier. Étant donné que ces chiffres sont des horodatages plus élevés, cela signifie que les nouveaux sont plus récents, ce qui place le plus récent enregistrement au début de la liste.Juste les morceaux importants
Comme la liste des chemins de fichiers qui en émerge
sort
a maintenant la réponse souhaitée que nous recherchons tout en haut. Reste à trouver un moyen de supprimer les autres enregistrements et de supprimer l'horodatage. Malheureusement, même GNUhead
ettail
n'acceptent pas les commutateurs pour les faire fonctionner sur une entrée délimitée par des valeurs nulles. Au lieu de cela, j'utilise une boucle while comme une sorte de pauvrehead
.Tout d'abord, je désactive
IFS
la liste des fichiers pour qu'elle ne soit pas soumise au fractionnement de mots. Ensuite, je disread
deux choses: n'interprétez pas les séquences d'échappement dans l'entrée (-r
) et l'entrée est délimitée par un octet nul (-d
); ici, la chaîne vide''
est utilisée pour indiquer "pas de délimiteur" aka délimité par null. Chaque enregistrement sera lu dans la variable derecord
sorte que chaque fois que lawhile
boucle itère, il ait un seul horodatage et un seul nom de fichier. Notez qu'il-d
s'agit d'une extension GNU; si vous n'avez qu'une norme,read
cette technique ne fonctionnera pas et vous avez peu de recours.Nous savons que la
record
variable comporte trois parties, toutes délimitées par des caractères de période. En utilisant l'cut
utilitaire, il est possible d'en extraire une partie.Ici, le dossier entier est transmis à
printf
et de là canalisé verscut
; en bash, vous pouvez simplifier davantage en utilisant une chaîne ici pourcut -d. -3f- <<<"$record"
de meilleures performances. Nous disonscut
deux choses: d'abord avec-d
cela il faut un délimiteur spécifique pour identifier les champs (comme avecsort
le délimiteur.
utilisé). Le secondcut
est chargé-f
d'imprimer uniquement les valeurs de champs spécifiques; la liste des champs est donnée sous la forme d'une plage3-
qui indique la valeur du troisième champ et de tous les champs suivants. Cela signifie quecut
va lire et ignorer tout jusqu'à et y compris la seconde.
qu'il trouve dans l'enregistrement, puis imprimer le reste, qui est la partie du chemin d'accès au fichier.Après avoir imprimé le dernier chemin de fichier, il n'est pas nécessaire de continuer:
break
quitte la boucle sans la laisser passer au deuxième chemin de fichier.La seule chose qui reste est en cours
tail
d' exécution sur le chemin de fichier renvoyé par ce pipeline. Vous avez peut-être remarqué dans mon exemple que j'ai fait cela en enfermant le pipeline dans un sous-shell; ce que vous n'avez peut-être pas remarqué, c'est que j'ai mis le sous-shell entre guillemets. Ceci est important car au final, même avec tous ces efforts pour être sûr pour tous les noms de fichiers, une extension de sous-shell non citée pourrait toujours casser les choses. Une explication plus détaillée est disponible si vous êtes intéressé. Le deuxième aspect important mais facilement ignoré de l'invocation detail
est que je lui ai fourni l'option--
avant d'étendre le nom du fichier. Cela demanderatail
qu'aucune autre option n'est spécifiée et que tout ce qui suit est un nom de fichier, ce qui permet de gérer en toute sécurité les noms de fichier commençant par-
.la source
tail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
head
ettail
n'acceptent pas les commutateurs pour les faire fonctionner sur une entrée délimitée par des valeurs nulles." Mon remplacement pourhead
:… | grep -zm <number> ""
.Si vous vous inquiétez des noms de fichiers avec des espaces,
la source
Vous pouvez utiliser:
La
$()
construction démarre un sous-shell qui exécute la commandels -1t
(répertoriant tous les fichiers dans l'ordre temporel, un par ligne) et les redirigehead -1
pour obtenir la première ligne (fichier).La sortie de cette commande (le fichier le plus récent) est ensuite transmise
tail
pour être traitée.Gardez à l'esprit que cela court le risque d'obtenir un répertoire s'il s'agit de l'entrée de répertoire la plus récente créée. J'ai utilisé cette astuce dans un alias pour modifier le fichier journal le plus récent (à partir d'un ensemble rotatif) dans un répertoire qui ne contenait que ces fichiers journaux.
la source
-1
n'est pas nécessaire,ls
fait cela pour vous quand il est dans un tuyau. Comparezls
etls|cat
, par exemple.Sur les systèmes POSIX, il n’existe aucun moyen d’obtenir l’entrée de répertoire "créée en dernier". Chaque entrée de répertoire a
atime
,mtime
etctime
, mais contrairement à Microsoft Windows, lectime
ne signifie pas CreationTime, mais "Heure du dernier changement d'état".Donc, le mieux que vous puissiez obtenir est de "suivre le dernier fichier récemment modifié", ce qui est expliqué dans les autres réponses. J'irais pour cette commande:
Notez les guillemets autour de la
ls
commande. Cela permet à l'extrait de fonctionner avec presque tous les noms de fichiers.la source
Si vous voulez juste voir le changement de taille de fichier, vous pouvez utiliser la montre.
la source
Dans
zsh
:Voir: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers , ici
m
dénote le temps de modificationm[Mwhms][-|+]n
, et le précédento
signifie qu'il est trié dans un sens (leO
trie dans l'autre sens). Cela.
signifie que les fichiers normaux. Dans les parenthèses[1]
choisit le premier élément. Pour en choisir trois[1,3]
, pour obtenir la plus ancienne utilisation[-1]
.C'est bien court et ne l'utilise pas
ls
.la source
Il y a probablement un million de façons de le faire, mais la façon dont je le ferais est la suivante:
Les bits entre les pointes (les guillemets comme des caractères) sont interprétés et le résultat renvoyé à la queue.
la source
Un simple:
fonctionne très bien pour moi.
Le problème est d'obtenir les fichiers générés après avoir démarré la commande tail. Mais si vous n'en avez pas besoin (comme toutes les solutions ci-dessus ne s'en soucient pas), l'astérisque est simplement une solution plus simple, l'OMI.
la source
la source
Quelqu'un l'a posté, puis l'a effacé pour une raison quelconque, mais c'est le seul qui fonctionne, alors ...
la source
ls
n'est pas la chose la plus intelligente à faire ...Explication:
la source
la source