Comment modifier les horodatages des dossiers de manière récursive vers le fichier le plus récent?


15

Je me demandais si quelqu'un savait comment modifier les horodatages des dossiers de manière récursive en fonction du dernier horodatage trouvé des fichiers dans ce dossier.

Ainsi, par exemple:

jon @ UbuntuPanther: / media / media / MP3s / Foo Fighters / (1997-05-20) La couleur et la forme $ ls -alF
55220 au total
drwxr-xr-x 2 jon jon 4096 2010-08-30 12:34 ./
drwxr-xr-x 11 jon jon 4096 2010-08-30 12:34 ../
-rw-r - r-- 1 jon jon 1694044 2010-04-18 00:51 Foo Fighters - Doll.mp3
-rw-r - r-- 1 jon jon 3151170 18/04/2010 00:51 Foo Fighters - Enough Space.mp3
-rw-r - r-- 1 jon jon 5004289 2010-04-18 00:52 Foo Fighters - Everlong.mp3
-rw-r - r-- 1 jon jon 5803125 2010-04-18 00:51 Foo Fighters - Février Stars.mp3
-rw-r - r-- 1 jon jon 4994903 18/04/2010 00:51 Foo Fighters - Hé, Johnny Park! .mp3
-rw-r - r-- 1 jon jon 4649556 18/04/2010 00:52 Foo Fighters - Monkey Wrench.mp3
-rw-r - r-- 1 jon jon 5216923 2010-04-18 00:51 Foo Fighters - My Hero.mp3
-rw-r - r-- 1 jon jon 4294291 2010-04-18 00:52 Foo Fighters - My Poor Brain.mp3
-rw-r - r-- 1 jon jon 6778011 18/04/2010 00:52 Foo Fighters - New Way Home.mp3
-rw-r - r-- 1 jon jon 2956287 2010-04-18 00:51 Foo Fighters - See You.mp3
-rw-r - r-- 1 jon jon 2730072 18/04/2010 00:51 Foo Fighters - Up in Arms.mp3
-rw-r - r-- 1 jon jon 6086821 2010-04-18 00:51 Foo Fighters - Walking After You.mp3
-rw-r - r-- 1 jon jon 3033660 2010-04-18 00:52 Foo Fighters - Wind Up.mp3

Le dossier "(1997-05-20) The Color and The Shape" aurait son horodatage réglé sur 2010-04-18 00:52.

Réponses:


20

Vous pouvez utiliser touch -rpour utiliser l'horodatage d'un autre fichier au lieu de l'heure actuelle (ou touch --reference=FILE)

Voici deux solutions. Dans chaque solution, la première commande change l'heure de modification du répertoire en celle du fichier le plus récent immédiatement en dessous, et la deuxième commande examine récursivement l'arborescence entière du répertoire. Accédez au répertoire ( cd '.../(1997-05-20) The Colour and The Shape') avant d'exécuter l'une des commandes.

En zsh (supprimez le Dpour ignorer les fichiers dot):

touch -r *(Dom[1]) .
touch -r **/*(Dom[1]) .

Sous Linux (ou plus généralement avec GNU find):

touch -r "$(find -mindepth 1 -maxdepth 1 -printf '%T+=%p\n' |
            sort |tail -n 1 | cut -d= -f2-)" .
touch -r "$(find -mindepth 1 -printf '%T+=%p\n' |
            sort |tail -n 1 | cut -d= -f2-)" .

Notez cependant que ceux-ci n'assument aucun caractère de nouvelle ligne dans les noms de fichiers.


Les commandes fonctionnent (j'ai utilisé celles de Linux) mais elles ne fonctionnent pas récursivement. Je l'ai exécuté dans mon dossier racine (/ media / media / MP3s) et pas de chance avec le reste des répertoires d'artistes là-dedans. Merci pour votre aide jusqu'à présent.
MonkeyWrench32

@ MonkeyWrench32: J'ai répondu à votre question telle que je l'ai comprise. Voulez-vous appliquer la commande à tous les répertoires situés en dessous /media/media/MP3s? Puis , en zsh: for d in /media/media/MP3s/**/*(/); do touch -r $d/*(Dom[1]) $d; done. Sans zsh (mais vraiment, utilisez zsh, c'est juste plus simple): mettez la commande dans un script et exécutez find /media/media/MP3s -type d -execdir /path/to/the/script \;.
Gilles 'SO- arrête d'être méchant'

La perfection! Vous êtes un maître zsh! : D
MonkeyWrench32

Si vous utilisez for d in ...comment pouvez-vous adapter les éléments suivants, de sorte que cela fonctionne également avec les dossiers contenant des espaces? (qui manque toujours dans ma solution)
rubo77

@ rubo77 Je ne comprends pas de quoi vous parlez. Toutes les solutions que j'ai publiées fonctionnent sur les noms de fichiers contenant des espaces (certaines échouent sur les noms de fichiers contenant des sauts de ligne). Notez que certaines de mes solutions nécessitent zsh, qui ne nécessite pas de guillemets doubles autour des substitutions de variables.
Gilles 'SO- arrête d'être méchant'

3

Ce n'est pas "récursivement", c'est juste changer tous les horodatages d'un dossier. Si c'est ce que vous voulez dire, il y a deux étapes.

stat -c '%Y' filenameaffichera l'horodatage de filename, et stat -c '%Y %n' *affichera l'horodatage et le nom de fichier de chaque fichier dans le dossier, donc cela trouvera le nom de fichier du fichier modifié le plus récemment dans le dossier actuel:

mostrecent="`stat -c '%Y %n' * | sort -n | tail -n1 | cut -d ' ' -f '2-'`"

Après réflexion, il existe un moyen beaucoup plus simple d'obtenir l'horodatage le plus élevé du dossier:

mostrecent="`ls -t | head -n1`"

Ensuite, vous souhaitez modifier tous les fichiers du dossier pour avoir le même horodatage que ce fichier. touch -r foo barchangera barpour avoir le même horodatage modifié que foo, donc cela changera tous les fichiers du dossier pour avoir le même horodatage modifié que votre fichier modifié le plus récemment:

touch -r "$mostrecent" *

Ainsi, le one-liner est:

touch -r "`ls -t | head -n1`" *

Analyser la sortie de ls est une mauvaise idée , même si j'avoue que c'est un cas tentant. Il est toujours risqué de lsmodifier les noms de fichiers contenant des caractères non ASCII. Si vous voulez des one-liners, utilisez zsh :-)
Gilles 'SO- arrêtez d'être méchant'

@Gilles C'est le cas, mais je n'ai pas encore rencontré de problème pour le faire; Je suppose que mes noms de fichiers ne contiennent pas de caractères non ASCII. Votre réponse est cependant meilleure
Michael Mrozek

2

J'ai rassemblé le travail et maintenant:

Ce serait un script qui change tous les répertoires à l'intérieur /tmp/test/de l'horodatage du fichier le plus récent à l'intérieur de chaque répertoire:

#!/bin/bash
if [ -z "$1" ] ; then
  echo 'ERROR: Parameter missing. specify the folder!'
  exit
fi
#MODE=tail # change to newest file
MODE=head # change to oldest file
for d in "$1"/*/; do
  echo "running on $d"
  find "$d" -type d -execdir \
    echo touch --reference="$(find "$d" -mindepth 1 -maxdepth 1 -printf '%T+=%p\n' \
                              | sort | "$MODE" -n 1 | cut -d= -f2-)" "$d" \;
    # remove echo to really run it
done

vous pouvez ajouter des fichiers de test dans / tmp comme ceci:

mkdir /tmp/test
cd /tmp/test
mkdir d1
mkdir d2
touch d1/text1.txt
sleep 1
touch d1/movie1.mov
touch d2/movie2.mov
sleep 1
touch d2/text2.txt
touch notthis.file


1
merci pour votre montage, ça marche! bien que l'écho supprime à "nouveau le, il semble d'abord qu'il manque, mais cela fonctionne.
rubo77

2

il suffit d'utiliser

find . -exec touch {} \;

0

J'utilise ce script pour définir récursivement les horodatages des dossiers sur le dernier contenu. (C'est très similaire à la réponse de Gilles):

#! /bin/bash

# Change mtime of directories to that of latest file (or dir) under it, recursively
# Empty leaf directories, or dirs with only symlinks get the $default_timestamp

default_timestamp='1980-01-01 00:00:00'

dir="$1"

[ -d "$dir" ] || { echo "Usage: $0 directory" >&2; exit 1; }

find "$dir" -depth -type d | while read d; do
    latest=$(find "$d" -mindepth 1 -maxdepth 1 \( -type f -o -type d \) -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
    if [ -n "$latest" ]; then
        touch -c -m -r "$latest" "$d" \
            || echo "ERROR setting mtime of '$d' using ref='$f'" >&2
    else
        touch -c -m -d "$default_timestamp" "$d" \
            || echo "ERROR setting mtime of '$d' to default '$default_timestamp'" >&2
    fi
done

0

J'ai pris la commande @Gilles zsh et l'ai améliorée pour qu'elle fonctionne dans les sous-dossiers, mais il semble que zsh soit terriblement inefficace pour la partie ** / * (FDod).

# Don't do this
for d in **/*(FDod); do touch -r "$d"/*(Dom[1]) "$d"; done

Les guillemets permettent aux entrées de répertoire contenant des espaces et des tabulations de fonctionner correctement. Le FD fait trouver des répertoires non vides, y compris ceux commençant par., Le od fait la recherche dans la profondeur d'abord de manière, de sorte que le dossier parent horodatages sont mis à jour correctement.

Lors des tests, j'ai remarqué que les performances et l'encombrement de la mémoire pour ** / * (FDod) étaient fous (plus de 1,4 Go pour seulement 650 000 dossiers), et il a lu le tout avant de commencer à toucher les dossiers. Après l'avoir remplacé par find / read, il est devenu beaucoup plus rapide, n'a pas brûlé la mémoire et a commencé presque immédiatement.

#! /usr/bin/env zsh
# Do this instead
find "$@" -depth -type d ! -empty -print0 |while IFS= read -r -d ''; do
    touch -r "$REPLY"/*(Dom[1]) "$REPLY"
done

Si vous ne l'exécutez pas dans un script, remplacez "$ @" par les dossiers à partir desquels vous souhaitez l'exécuter.


-1

Je n'aimais pas vraiment l'obscurité des commandes shell et je l'ai fait rapidement en python.

Il définit récursivement tous les répertoires mtimes sur le dernier fichier / répertoire mtime non exclu à l'intérieur.

import os
import sys

EXCLUDE_FILES = ['.DS_Store']
EXCLUDE_DIRS = ['.git', '.hg']

def inheritMTime(path):
    for root, dirs, files in os.walk(path, topdown=False, followlinks=False):
        currentMaxMtime = None
        for subpath in files:
            if subpath in EXCLUDE_FILES:
                print "ignore file", os.path.join(root, subpath)
                continue
            currentMaxMtime = max(currentMaxMtime, os.lstat(os.path.join(root, subpath)).st_mtime)
        for subpath in dirs:
            if subpath in EXCLUDE_DIRS:
                print "ignore dir", os.path.join(root, subpath)
                continue
            currentMaxMtime = max(currentMaxMtime, os.lstat(os.path.join(root, subpath)).st_mtime)
        if currentMaxMtime is not None:
            os.utime(root, (currentMaxMtime, currentMaxMtime))

if __name__ == "__main__":
    for dir in os.listdir(sys.argv[1]):
        if os.path.isdir(dir):
            current = os.path.join(sys.argv[1], dir)
            inheritMTime(current)
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.