Як змінити часові позначки папок рекурсивно на найновіший файл?


15

Мені було цікаво, чи хтось знає, як змінювати часові позначки папок рекурсивно на основі останньої часової позначки, знайденої у файлах у цій папці.

Так, наприклад:

jon @ UbuntuPanther: / медіа / медіа / MP3 / Foo Fighters / (1997-05-20) Колір і форма $ ls -alF
всього 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 2010-04-18 00:52 Foo Fighters - Everlong.mp3
-rw-r - r-- 1 jon jon 5803125 2010-04-18 00:51 Foo Fighters - February Stars.mp3
-rw-r - r-- 1 jon jon 4994903 2010-04-18 00:51 Foo Fighters - Hey, Johnny Park! .mp3
-rw-r - r-- 1 jon jon 4649556 2010-04-18 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 2010-04-18 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 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

Папка "(1997-05-20) Колір і форма" матиме часову позначку на 2010-04-18 00:52.

Відповіді:


20

Ви можете використовувати touch -rчасову позначку іншого файлу замість поточного часу (або touch --reference=FILE)

Ось два рішення. У кожному рішенні перша команда змінює час модифікації каталогічного файлу на найновіший файл безпосередньо під ним, а друга команда рекурсивно дивиться на все дерево каталогів. Перейдіть до каталогу ( cd '.../(1997-05-20) The Colour and The Shape') перед запуском будь-якої команди.

У zsh (видаліть Dігнорувати крапкові файли):

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

У Linux (або загалом із 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-)" .

Однак зауважте, що вони не передбачають символів нового рядка у назвах файлів.


Команди працюють (я використовував Linux), але це не працює рекурсивно. Я запустив його в свою кореневу папку (/ media / media / MP3s), і не було такої удачі з рештою каталогів виконавців там. Дякую за допомогу досі.
MonkeyWrench32

@ MonkeyWrench32: Я відповів на ваше запитання, коли я зрозумів це. Ви хочете застосувати команду до кожного каталогу внизу /media/media/MP3s? Тоді в zsh : for d in /media/media/MP3s/**/*(/); do touch -r $d/*(Dom[1]) $d; done. Без zsh (але дійсно, використовуйте zsh, це просто простіше): введіть команду в сценарій і запустіть find /media/media/MP3s -type d -execdir /path/to/the/script \;.
Жил "ТАК - перестань бути злим"

Вдосконалення! Ви майстер zsh! : D
MonkeyWrench32

Якщо ви використовуєте, for d in ...як ви можете адаптувати наступне, щоб воно також працювало з папками, які містять пробіли? (цього ще немає в моєму рішенні)
rubo77

@ rubo77 Я не розумію, про що ти говориш. Усі розміщені нами рішення працюють над іменами файлів, що містять пробіли (деякі з них не відповідають іменам файлів, що містять нові рядки). Зауважте, що деякі мої рішення потребують zsh, що не потребує подвійних лапок навколо змінних підстановок.
Жил "ТАК - перестань бути злим"

3

Це не "рекурсивно", це просто зміна всіх часових позначок у папці. Якщо це ви маєте на увазі, є два кроки.

stat -c '%Y' filenameвиведе часову марку filenameі stat -c '%Y %n' *видасть часову марку та ім’я кожного файлу в папці, тож знайдеться ім'я останнього зміненого файла у поточній папці:

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

На другий думки, є сторонній простий спосіб , щоб отримати найвищу позначку в папці:

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

Тоді ви хочете змінити всі файли в папці, щоб вони мали ту саму часову позначку, що і цей файл. touch -r foo barзміниться, barщоб мати таку ж змінену часову позначку, як foo, тому це змінить усі файли в папці, щоб вони мали таку ж змінену часову позначку, як і ваш останній змінений файл:

touch -r "$mostrecent" *

Отже, однолінійним є:

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

Розбір результатів ls - погана ідея , хоча я визнаю, що це заманливо. Це все ще ризиковано, якщо lsмангли назви файлів, що містять символи, що не належать до ASCII. Якщо ви хочете однолінійки, використовуйте zsh :-)
Жил "ТАК - перестаньте бути злим"

@Gilles Це так, але я ще не зіткнувся з проблемою, роблячи це; Я думаю, оскільки мої імена файлів не містять символів, що не належать до ASCII. Хоча ваша відповідь краща
Майкл Мрозек

2

Я склав роботу разом і зараз:

Це був би сценарій, який змінює всі каталоги всередині /tmp/test/часової позначки найновішого файлу всередині кожного каталогу:

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

Ви можете додати кілька тестових файлів у / tmp так:

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
дякую за вашу редакцію, вона працює! хоча відлуння "знову видаляється , воно спочатку виглядає, ніби його немає, але воно працює.
rubo77


0

Я використовую цей скрипт для рекурсивного встановлення часових позначок папок на останній вміст. (Це дуже схоже на відповідь Жиля):

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

Я взяв команду @Gilles zsh і вдосконалив її для роботи в підпапках, але, здається, zsh жахливо неефективний для частини ** / * (FDod).

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

Цитати дозволяють коректно працювати записам у каталозі, що містять пробіли та вкладки. FD змушує знаходити не порожні каталоги, включаючи ті, що починаються з., Od змушує його здійснювати пошук по -перше , так що часові позначки батьківської папки оновлюються належним чином.

Під час тестування я помітив, що сліди продуктивності та пам’яті для ** / * (FDod) були божевільними (понад 1,4 ГБ для всього 650 к папок), і він прочитав цілу партію, перш ніж почати торкатися папок. Після заміни на пошук / читання він став набагато швидшим, не спалив пам’ять і почав майже відразу.

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

Якщо ви не запускаєте його в сценарії, замініть "$ @" папками, з яких потрібно запустити його.


-1

Мені не дуже сподобалася незрозумілість команд оболонки і швидко це зробили в python.

Він рекурсивно встановлює всі директорії mtimes до найновішого не виключеного файлу / dir mtime всередині.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.