Comment écrire un script pour déplacer uniquement les 20 fichiers les plus anciens d'un dossier à un autre? Existe-t-il un moyen de récupérer les fichiers les plus anciens dans un dossier?
bash
shell
shell-script
files
timestamps
user11598
la source
la source
atime
(dernier accès),ctime
(dernier changement d'autorisation) etmtime
(dernière modification) ... par exemple.ls -t
and find'sprintf "%T"
usemtime
... Il semble, selon ce lien , que mesext4
partitions sont capables de gérer une date de création, maisls
etfind
etstat
n'ont pas (encore) les options appropriées ...Réponses:
L'analyse de la sortie de
ls
n'est pas fiable .À la place, utilisez
find
pour localiser les fichiers etsort
les classer par horodatage. Par exemple:Que fait tout ça?
Tout d'abord, les
find
commandes localisent tous les fichiers et répertoires dans le répertoire courant (.
), mais pas dans les sous-répertoires du répertoire courant (-maxdepth 1
), puis imprime:L'horodatage est important. Le
%T@
spécificateur de format pour-printf
se décompose enT
, qui indique "Dernière heure de modification" du fichier (mtime) et@
, qui indique "Secondes depuis 1970", y compris les fractions de seconde.L'espace n'est qu'un délimiteur arbitraire. Le chemin d'accès complet au fichier est pour que nous puissions y faire référence plus tard, et le caractère NULL est un terminateur car il s'agit d'un caractère illégal dans un nom de fichier et nous permet ainsi de savoir avec certitude que nous avons atteint la fin du chemin vers le fichier.
J'ai inclus
2>/dev/null
afin que les fichiers auxquels l'utilisateur n'est pas autorisé à accéder soient exclus, mais les messages d'erreur les excluant sont supprimés.Le résultat de la
find
commande est une liste de tous les répertoires du répertoire courant. La liste est dirigée verssort
laquelle est chargé de:-z
Traitez NULL comme le caractère de fin de ligne au lieu de la nouvelle ligne.-n
Trier numériquementPuisque secondes depuis 1970 monte toujours, nous voulons le fichier dont l'horodatage était le plus petit nombre. Le premier résultat de
sort
sera la ligne contenant le plus petit horodatage numéroté. Il ne reste plus qu'à extraire le nom du fichier.Les résultats du
find
,sort
pipeline est passé par la substitution de processus pourwhile
lequel il est lu comme si elle était un fichier sur stdin.while
à son tour, invoqueread
pour traiter l'entrée.Dans le contexte de,
read
nous définissons laIFS
variable sur rien, ce qui signifie que les espaces blancs ne seront pas interprétés de manière inappropriée comme un délimiteur.read
est dit-r
, ce qui désactive l' expansion d'échappement, et-d $'\0'
, ce qui rend le séparateur de fin de ligne NULL, correspondant à la sortie de notrefind
,sort
pipeline.Le premier bloc de données, qui représente le chemin de fichier le plus ancien précédé de son horodatage et d'un espace, est lu dans la variable
line
. Ensuite, la substitution de paramètres est utilisée avec l'expression#*
, qui remplace simplement tous les caractères depuis le début de la chaîne jusqu'au premier espace, y compris l'espace, sans rien. Cela supprime l'horodatage de modification, ne laissant que le chemin d'accès complet au fichier.À ce stade, le nom du fichier est stocké
$file
et vous pouvez en faire ce que vous voulez. Lorsque vous avez fini de faire quelque chose avec$file
l'while
instruction, la boucleread
sera exécutée et la commande sera exécutée à nouveau, en extrayant le morceau suivant et le nom de fichier suivant.N'y a-t-il pas un moyen plus simple?
Non. Les moyens plus simples sont bogués.
Si vous utilisez
ls -t
et dirigez vershead
outail
(ou quoi que ce soit ), vous casserez les fichiers avec des retours à la ligne dans les noms de fichiers. Si vous avezmv $(anything)
ensuite des fichiers avec un espace dans le nom, cela entraînera une rupture. Si vous avezmv "$(anything)"
ensuite des fichiers avec des retours à la ligne dans le nom, cela entraînera une rupture. Si vousread
ne le faites pas,-d $'\0'
vous allez casser des fichiers avec des espaces dans leurs noms.Peut-être que dans certains cas spécifiques, vous savez avec certitude qu'une méthode plus simple est suffisante, mais vous ne devriez jamais écrire des hypothèses comme celle-ci dans des scripts si vous pouvez éviter de le faire.
Solution
Appelez comme:
Pour déplacer les 20 fichiers les plus anciens de
/var/log/foo/
vers/mnt/backup/
.Notez que j'inclus des fichiers et des répertoires. Pour les fichiers, ajoutez uniquement
-type f
à l'find
invocation.Merci
Merci à enzotib et Павел Танков pour les améliorations apportées à cette réponse.
la source
-n
. Au moins dans ma version, il ne trie pas correctement les nombres décimaux. Vous devez soit supprimer le point de la date, soit utiliser des-printf '%TY-%Tm-%TdT%TH:%TM:%TS %p\0' | sort -rz
dates ISO ou autre chose..
doit être hors de propos pour vous.) Ce serait plus clair à diresort -z -n -t. -k1
.%TS
affiche également une "partie fractionnaire" qui serait sous la forme00.0000000000
, vous perdez donc également la granularité. Un GNU récentsort
pourrait résoudre ce problème en utilisant-V
un "tri de version", qui traitera ce type de virgule flottante comme prévu.%T@
fonctionnerait également, car il est complété par zéro.C'est plus simple dans zsh, où vous pouvez utiliser le
Om
qualificatif glob pour trier les correspondances par date (le plus ancien en premier) et le[1,20]
qualificatif pour ne conserver que les 20 premières correspondances:Ajouter le
D
qualificatif si vous souhaitez également inclure des fichiers de points. Ajoutez.
si vous souhaitez faire correspondre uniquement les fichiers standard et non les répertoires.Si vous n'avez pas zsh, voici un one-liner Perl (vous pouvez le faire en moins de 80 caractères, mais à des frais supplémentaires de clarté):
Avec seulement des outils POSIX ou même bash ou ksh, le tri des fichiers par date est une tâche difficile. Vous pouvez le faire facilement avec
ls
, mais l'analyse de la sortie dels
est problématique, donc cela ne fonctionne que si les noms de fichiers ne contiennent que des caractères imprimables autres que les retours à la ligne.la source
Combinez la
ls -t
sortie avectail
ouhead
.Exemple simple, qui ne fonctionne que si tous les noms de fichiers contiennent uniquement des caractères imprimables autres que des espaces et
\[*?
:la source
ls -1Atr
touch $'foo\n*'
. Que se passe-t-il si vous exécutez mv "$ (ls)" avec ce fichier là?Vous pouvez utiliser GNU find pour cela:
Où find affiche l'heure de modification (en secondes à partir de 1970) et le nom de chaque fichier du répertoire courant, la sortie est triée selon le premier champ, les 20 plus anciennes sont filtrées et déplacées vers
dest_dir
. Supprimez leecho
si vous avez testé la ligne de commande.la source
Personne n'a (encore) publié d'exemple bash qui répond aux caractères de nouvelle ligne intégrés (quoi que ce soit) dans le nom de fichier, alors en voici un. Il déplace les 3 fichiers réguliers les plus anciens (mdate)
Ceci est l' extrait de données de test
Voici l' extrait des résultats de vérification
la source
C'est plus simple à faire avec GNU
find
. Je l'utilise tous les jours sur mon DVR Linux pour supprimer des enregistrements de mon système de vidéosurveillance datant de plus d'un jour.Voici la syntaxe:
N'oubliez pas que cela
find
définit un jour comme 24 heures à partir du moment de l'exécution. Par conséquent, les fichiers modifiés pour la dernière fois à 23 h ne seront pas supprimés à 1 h.Vous pouvez même combiner
find
aveccron
, afin que les suppressions puissent être planifiées automatiquement en exécutant la commande suivante en tant que root:Vous pouvez toujours obtenir plus d'informations sur
find
en consultant sa page de manuel:la source
comme les autres réponses ne correspondent pas à mon objectif et aux questions, ce shell est testé sur CentOS 7:
la source