Jak zmienić rekursywnie znaczniki czasu folderu do najnowszego pliku?


15

Zastanawiałem się, czy ktoś wie, jak rekursywnie zmieniać znaczniki czasu folderów na podstawie najnowszego znacznika czasu znalezionego w plikach w tym folderze.

Na przykład:

jon @ UbuntuPanther: / media / media / MP3 / Foo Fighters / (1997-05-20) Kolor i kształt $ ls -alF
55220 ogółem
drwxr-xr-x 2 jon jon 4096 30.08.2010 12:34 ./
drwxr-xr-x 11 jon jon 4096 30.08.2010 12:34 ../
-rw-r - r-- 1 jon jon 1694044 18.04.2010 2010 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 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 - Hej, Johnny Park! .mp3
-rw-r - r-- 1 jon jon 4649556 18.04.2010 2010 00:52 Foo Fighters - Monkey Wrench.mp3
-rw-r - r-- 1 jon jon 5216923 18.04.2010 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 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 18.04.2010 2010 00:51 Foo Fighters - Up in Arms.mp3
-rw-r - r-- 1 jon jon 6086821 18.04.2010 00:51 Foo Fighters - Walking After You.mp3
-rw-r - r-- 1 jon jon 3033660 18.04.2010 00:52 Foo Fighters - Wind Up.mp3

Folder „(1997-05-20) Kolor i kształt” miałby swój znacznik czasu ustawiony na 2010-04-18 00:52.

Odpowiedzi:


20

Możesz użyć touch -rznacznika czasu innego pliku zamiast bieżącego czasu (lub touch --reference=FILE)

Oto dwa rozwiązania. W każdym rozwiązaniu pierwsze polecenie zmienia czas modyfikacji katalogu na czas najnowszego pliku znajdującego się bezpośrednio pod nim, a drugie polecenie rekurencyjnie przegląda całe drzewo katalogów. Przejdź do katalogu ( cd '.../(1997-05-20) The Colour and The Shape') przed uruchomieniem dowolnego polecenia.

W zsh (usuń Dpliki kropek, aby zignorować):

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

W systemie Linux (lub bardziej ogólnie z 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-)" .

Należy jednak pamiętać, że te nie zakładają znaków nowej linii w nazwach plików.


Komendy działają (korzystałem z Linuksa), ale nie działają rekurencyjnie. Uruchomiłem go w folderze głównym (/ media / media / MP3) i nie miałem szczęścia z resztą katalogów artystów. Dziękuję za pomoc do tej pory.
MonkeyWrench32

@ MonkeyWrench32: Odpowiedziałem na twoje pytanie tak, jak je rozumiem. Czy chcesz zastosować polecenie do każdego katalogu poniżej /media/media/MP3s? Następnie w zsh: for d in /media/media/MP3s/**/*(/); do touch -r $d/*(Dom[1]) $d; done. Bez zsh (ale tak naprawdę użyj zsh, to jest po prostu prostsze): umieść polecenie w skrypcie i uruchom find /media/media/MP3s -type d -execdir /path/to/the/script \;.
Gilles „SO- przestań być zły”

Doskonałość! Jesteś mistrzem Zsh! : D
MonkeyWrench32

Jeśli użyjesz, w for d in ...jaki sposób możesz dostosować następujące elementy, aby działało to również z folderami zawierającymi spacje? (którego wciąż brakuje w moim rozwiązaniu)
rubo77,

@ rubo77 Nie rozumiem o czym mówisz. Wszystkie opublikowane przeze mnie rozwiązania działają na nazwach plików zawierających spacje (niektóre z nich zawodzą na nazwach plików zawierających nowe wiersze). Zauważ, że niektóre z moich rozwiązań wymagają zsh, który nie wymaga podwójnych cudzysłowów wokół podstawień zmiennych.
Gilles „SO- przestań być zły”

3

To nie jest „rekurencyjnie”, po prostu zmienia wszystkie znaczniki czasu w folderze. Jeśli to masz na myśli, są dwa kroki.

stat -c '%Y' filenamewyświetli znacznik czasu filenamei stat -c '%Y %n' *wyświetli znacznik czasu i nazwę pliku każdego pliku w folderze, dzięki czemu znajdzie nazwę ostatnio zmodyfikowanego pliku w bieżącym folderze:

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

Z drugiej strony istnieje znacznie łatwiejszy sposób na uzyskanie najwyższego znacznika czasu w folderze:

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

Następnie chcesz zmienić wszystkie pliki w folderze, aby miały ten sam znacznik czasu co ten plik. touch -r foo barzmieni się tak, baraby mieć taki sam zmodyfikowany znacznik czasu jak foo, więc spowoduje to zmianę wszystkich plików w folderze na taki sam zmodyfikowany znacznik czasu, jak ostatnio zmodyfikowany plik:

touch -r "$mostrecent" *

Tak więc jedna linijka to:

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

Analiza wyniku ls jest złym pomysłem , choć przyznaję, że to kuszący przypadek. Nadal ryzykowne jest, jeśli lsnazwy plików mangles zawierające znaki spoza ASCII. Jeśli chcesz mieć jedno-linijki, użyj zsh :-)
Gilles 'SO- przestań być zły'

@Gilles Tak, ale jeszcze nie napotkałem problemu; Przypuszczam, ponieważ moje nazwy plików nie zawierają znaków spoza ASCII. Twoja odpowiedź jest lepsza
Michael Mrozek

2

Złożyłem pracę i teraz:

Byłby to skrypt, który zmienia wszystkie katalogi wewnątrz /tmp/test/na znacznik czasu najnowszego pliku w każdym katalogu:

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

możesz dodać kilka plików testowych w / tmp w następujący sposób:

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
dzięki za edycję, to działa! chociaż echo "ponownie usuwa , najpierw wygląda, jakby go brakowało, ale działa.
rubo77,


0

Używam tego skryptu, aby rekurencyjnie ustawiać znaczniki czasu folderów na najnowszą zawartość. (Jest to bardzo podobne do odpowiedzi Gillesa):

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

Wziąłem komendę @Gilles zsh i ulepszyłem ją do pracy w podfolderach, ale wygląda na to, że zsh jest strasznie nieefektywny dla części ** / * (FDod).

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

Cytaty umożliwiają prawidłowe działanie pozycji katalogu zawierających spacje i tabulatory. FD sprawia, że ​​wyszukuje niepuste katalogi, w tym te zaczynające się od., Od od przeszukuje w pierwszej kolejności , aby znaczniki czasu folderów nadrzędnych były odpowiednio aktualizowane.

Podczas testowania zauważyłem, że wydajność i wielkość pamięci dla ** / * (FDod) była szalona (ponad 1,4 GB dla zaledwie 650 tys. Folderów) i przeczytał całą część, zanim zaczął dotykać folderów. Po zastąpieniu go funkcją znajdź / odczyt stał się znacznie szybszy, nie spalił pamięci i zaczął działać niemal natychmiast.

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

Jeśli nie uruchamiasz go w skrypcie, zamień „$ @” na foldery, z których chcesz go uruchomić.


-1

Naprawdę nie podobało mi się niejasność poleceń powłoki i szybko zrobiłem to w Pythonie.

Rekurencyjnie ustawia wszystkie mtimes katalogu na najnowszy niewykluczony plik / dir mtime wewnątrz.

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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.