Как рекурсивно изменить метки времени папок на самый новый файл?


15

Мне было интересно, если кто-нибудь знает, как рекурсивно изменять временные метки папок на основе последней найденной метки времени файлов в этой папке.

Так, например:

jon @ UbuntuPanther: / media / media / MP3s / Foo Fighters / (1997-05-20) Цвет и форма $ ls -alF
всего 55220
drwxr-xr-x 2 Джон Джон 4096 2010-08-30 12:34 ./
drwxr-xr-x 11 Джон Джон 4096 2010-08-30 12:34 ../
-rw-r - r-- 1 Джон Джон 1694044 2010-04-18 00:51 Foo Fighters - Doll.mp3
-rw-r - r-- 1 Джон Джон 3151170 2010-04-18 00:51 Foo Fighters - Достаточно Space.mp3
-rw-r - r-- 1 Джон Джон 5004289 2010-04-18 00:52 Foo Fighters - Everlong.mp3
-rw-r - r-- 1 Джон Джон 5803125 2010-04-18 00:51 Foo Fighters - Февраль Stars.mp3
-rw-r - r-- 1 Джон Джон 4994903 2010-04-18 00:51 Foo Fighters - Привет, Джонни Парк! .mp3
-rw-r - r-- 1 Джон Джон 4649556 2010-04-18 00:52 Foo Fighters - Обезьяна Wrench.mp3
-rw-r - r-- 1 Джон Джон 5216923 2010-04-18 00:51 Foo Fighters - My Hero.mp3
-rw-r - r-- 1 Джон Джон 4294291 2010-04-18 00:52 Foo Fighters - My Poor Brain.mp3
-rw-r - r-- 1 Джон Джон 6778011 2010-04-18 00:52 Foo Fighters - New Way Home.mp3
-rw-r - r-- 1 Джон Джон 2956287 2010-04-18 00:51 Foo Fighters - Увидимся.mp3
-rw-r - r-- 1 Джон Джон 2730072 2010-04-18 00:51 Foo Fighters - Up в Arms.mp3
-rw-r - r-- 1 Джон Джон 6086821 2010-04-18 00:51 Foo Fighters - Идем за тобой.mp3
-rw-r - r-- 1 Джон Джон 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 :-)
Жиль "ТАК - прекрати быть злым"

@ Жиль Да, но я еще не столкнулся с проблемой, делая это; Я полагаю, потому что мои имена файлов не содержат не-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.