Come posso modificare in modo ricorsivo i timestamp delle cartelle nel file più recente?


15

Mi chiedevo se qualcuno sa come modificare in modo ricorsivo i timestamp delle cartelle in base all'ultimo timestamp trovato dei file in quella cartella.

Quindi per esempio:

jon @ UbuntuPanther: / media / media / MP3s / Foo Fighters / (1997-05-20) The Color and The Shape $ ls -alF
totale 55220
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 2010-04-18 00:51 Foo Fighters - Enough Space.mp3
-rw-r - r-- 1 jon jon 5004289 18-04-2010 00:52 Foo Fighters - Everlong.mp3
-rw-r - r-- 1 jon jon 5803125 18-04-2010 00:51 Foo Fighters - February Stars.mp3
-rw-r - r-- 1 jon jon 4994903 18-04-2010 00:51 Foo Fighters - Ehi, 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 18-04-2010 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 18-04-2010 00:51 Foo Fighters - See You.mp3
-rw-r - r-- 1 jon jon 2730072 2010-04-18 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

La cartella "(1997-05-20) The Color and The Shape" avrebbe il timestamp impostato su 18-04-2010 00:52.

Risposte:


20

È possibile utilizzare touch -rper utilizzare il timestamp di un altro file anziché l'ora corrente (o touch --reference=FILE)

Ecco due soluzioni. In ciascuna soluzione, il primo comando modifica il tempo di modifica della directory in quello del file più recente immediatamente sotto di esso e il secondo comando esamina ricorsivamente l'intero albero di directory. Passare alla directory ( cd '.../(1997-05-20) The Colour and The Shape') prima di eseguire uno qualsiasi dei comandi.

In zsh (rimuovere il Dper ignorare i file punto):

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

Su Linux (o più in generale con 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-)" .

Tuttavia, si noti che quelli non assumono caratteri di nuova riga nei nomi dei file.


I comandi funzionano (ho usato quelli Linux) ma non funzionano in modo ricorsivo. L'ho eseguito nella mia cartella principale (/ media / media / MP3s) e nessuna fortuna con il resto delle directory degli artisti lì dentro. Grazie per il tuo aiuto finora.
MonkeyWrench32

@ MonkeyWrench32: ho risposto alla tua domanda come ho capito. Vuoi applicare il comando a tutte le directory sottostanti /media/media/MP3s? Poi, nel zsh: for d in /media/media/MP3s/**/*(/); do touch -r $d/*(Dom[1]) $d; done. Senza zsh (ma davvero, usa zsh, è solo più semplice): inserisci il comando in uno script ed esegui find /media/media/MP3s -type d -execdir /path/to/the/script \;.
Gilles 'SO- smetti di essere malvagio'

Perfezione! Sei un maestro zsh! : D
MonkeyWrench32

Se usi for d in ...come puoi adattare quanto segue, quindi funzionerà anche con le cartelle che contengono spazi? (che manca ancora nella mia soluzione)
rubo77

@ rubo77 Non capisco di cosa stai parlando. Tutte le soluzioni che ho pubblicato funzionano su nomi di file contenenti spazi (alcuni falliscono su nomi di file contenenti newline). Nota che alcune delle mie soluzioni richiedono zsh, che non richiede doppie virgolette per le sostituzioni variabili.
Gilles 'SO- smetti di essere malvagio' il

3

Non è "ricorsivamente", sta solo cambiando tutti i timestamp in una cartella. Se è questo che intendi, ci sono due passaggi.

stat -c '%Y' filenamegenererà il timestamp di filename, e stat -c '%Y %n' *produrrà il timestamp e il nome del file di ogni file nella cartella, così questo troverà il nome del file modificato più di recente nella cartella corrente:

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

Ripensandoci, c'è un modo più semplice per ottenere il timestamp più alto nella cartella:

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

Quindi si desidera modificare tutti i file nella cartella per avere lo stesso timestamp di quel file. touch -r foo barcambierà barper avere lo stesso timestamp modificato foo, quindi questo cambierà tutti i file nella cartella per avere lo stesso timestamp modificato del tuo file modificato più di recente:

touch -r "$mostrecent" *

Quindi, il one-liner è:

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

Analizzare l'output di ls è una cattiva idea , anche se ammetto che sia un caso allettante. È ancora rischioso se lsi nomi dei file mangles contengono caratteri non ASCII. Se vuoi una
battuta

@Gilles Lo è, ma devo ancora imbattermi in un problema nel farlo; Suppongo perché i miei nomi di file non contengono caratteri non ASCII. La tua risposta è comunque migliore
Michael Mrozek

2

Metto insieme il lavoro e ora:

Questo sarebbe uno script che cambia tutte le directory all'interno /tmp/test/ del timestamp del file più recente all'interno di ogni directory:

#!/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

puoi aggiungere alcuni file di test in / tmp in questo modo:

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
grazie per la tua modifica, funziona! sebbene l'eco rimuova di "nuovo, sembra prima che manchi, ma funziona.
rubo77,

2

basta usare

find . -exec touch {} \;

0

Uso questo script per impostare in modo ricorsivo i timestamp delle cartelle sul contenuto più recente. (È molto simile alla risposta di 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

Ho preso il comando @Gilles zsh e l'ho migliorato per funzionare nelle sottocartelle, ma sembra che zsh sia terribilmente inefficiente per la parte ** / * (FDod).

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

Le virgolette consentono alle voci della directory che contengono spazi e schede di funzionare correttamente. L'FD gli consente di trovare directory non vuote, comprese quelle che iniziano con., L'od lo fa cercare in approfondito , in modo che i timestamp della cartella padre vengano aggiornati correttamente.

Durante i test, ho notato che il footprint delle prestazioni e della memoria per ** / * (FDod) era folle (oltre 1,4 GB per solo 650k cartelle) e leggeva l'intero lotto prima di iniziare a toccare le cartelle. Dopo averlo sostituito con find / read è diventato molto più veloce, non ha bruciato memoria e si è avviato quasi subito.

#! /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

Se non lo si esegue in uno script, sostituire "$ @" con le cartelle da cui si desidera eseguirlo.


-1

Non mi piaceva molto l'oscurità dei comandi della shell e l'ho fatto rapidamente in Python.

Imposta in modo ricorsivo tutte le directory mtime sul più recente file / dir mtime non escluso all'interno.

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.