Рекурсивно соприкасается с последней измененной датой-временем каталогов и той же датой-временем последнего измененного файла внутри

1269
Phil

Ранее я запускал dos2unixдля каждого *.phpфайла на моем сервере рекурсивную команду:

find . -name '*.php' -type f -exec dos2unix --keepdate {} + 

У всех файлов, которые были обработаны, dos2unixбыла изменена дата и время последнего изменения, но в родительском каталоге, содержащем такие файлы, дата и время последнего изменения были обновлены до момента, когда я выполнил команду.

Как при помощи одного вкладыша рекурсивно дотронуться до каталогов, установив дату последнего изменения каталогов равной дате и времени последнего измененного файла в каталоге?

Из этого:

dir_a/ 2013-05-13 14:05 abc.php 2012-09-01 12:34 def.php 2012-09-15 23:45 dir_b/ 2013-05-13 14:05 uvw.php 2012-10-01 01:23 xyz.php 2012-10-08 09:10 

К этому:

dir_a/ 2012-09-15 23:45 abc.php 2012-09-01 12:34 def.php 2012-09-15 23:45 dir_b/ 2012-10-08 09:10 uvw.php 2012-10-01 01:23 xyz.php 2012-10-08 09:10 

Сервер работает на CentOS 5.6.

1

2 ответа на вопрос

2
Nykakin

Может быть, что-то вроде этого:

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done 

Объяснение:

Чтобы передать дату модификации из одного файла в другой, нам нужно выполнить:

touch file1 -mr file2 

Сначала мы должны найти подкаталоги:

find . -type d -print0 | while read -r -d '' dir; do echo "$dir"; done 

В этом случае -execопция будет слишком сложной, поэтому я использую подход с чтением. Вы должны убедиться, что выводите данные с findпомощью в NULкачестве разделителя записей, и попросите readразделить на них ( -d '').

Для поиска файлов с последней датой модификации я могу использовать:

find ./ -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2- 

Сочетая это:

find . -mindepth 1 -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";echo "$dir <- $file"; done 

Наконец, добавление прикосновения:

find . -mindepth 1 -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";touch "$dir" -mr "$file"; done 

Я использую -mindepthопцию здесь, потому что я запустил эту команду из каталога, содержащего dir_a, у которого нет файла для чтения даты. Чтобы устранить эту проблему, я могу использовать if-else, чтобы убедиться, что я не пытаюсь прочитать время из несуществующего файла:

find . -type d -print0 | while read -r -d '' dir; do file="$(find "$dir" -maxdepth 1 -type f -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2-)";if [ -n "$file" ]; then touch "$dir" -mr "$file"; fi; done 
Спасибо за ответ, это сработало почти идеально, только сбой, когда `touch` пытается получить последний измененный datetime из файла с пробелами в имени файла. Phil 10 лет назад 0
@Phil Да, это возможно из-за (сломанной) конструкции `$ (find…)` в середине. Попробуйте заменить `-f2` на` -f2-`и окружить` $ () `двойными кавычками. slhck 10 лет назад 2
@slhck Это решило проблему с `touch`, но теперь я заметил, что` find` не работает, когда имя каталога содержит пробелы, можете ли вы указать, как это исправить? Благодарю. Phil 10 лет назад 0
@Phil Правда, это `найти… | в то время как` был сломан. Вам нужно вызвать его с помощью `-print0 | пока читаем… -d '' `и заключаем в кавычки` "$ dir" во внутреннем цикле. slhck 10 лет назад 1
1
terdon

Это должно сделать это:

find -mindepth 1 -type d -print0 | while IFS= read -r -d '' dir; do  time=$( find "$dir" -mindepth 1 -maxdepth 1 -type f -exec stat -c %Y {} \; |  sort -nk2 | tail -n 1 ); touch -d @$time "$dir";  done 

ОБЪЯСНЕНИЕ:

  • find -mindepth 1 -type d | while IFS= read -r -d '' dir; do: перебрать все каталоги, кроме текущего ( -mindepth 1). Использование whileцикла с IFS=и -rфлагом позволяет убедиться, что команда работает с именами каталогов, которые содержат пробелы или новые строки ( \n) или другие странные символы. В -d ''устанавливает разделитель для нулевого символа, чтобы он мог работать с ФАЙНД -print0опцией.

  • find $dir -mindepth 1 -maxdepth 1 -type f -exec stat --printf="%Y\n" {} \;: Найти все файлы каждого каталога $dirи запустить, statкоторый печатает их дату модификации в секундах с начала эпохи .

  • sort -nk2 | tail -n 1 Это просто сортирует даты и сохраняет самые новые. Последние две части вместе дают время модификации самого нового файла, который сохраняется в переменной $time.

  • touch -d @$time $dir: здесь мы используем touchкоманду для установки времени модификации каждого каталога. @Есть сказать, touchчто мы повторно давая время в секундах с начала эпохи, это стандарт GNU Coreutils особенность .

Спасибо за ответ, но я получаю много ошибок `touch: invalid date \` @ '`при запуске. Phil 10 лет назад 0
@Phil Проверьте, содержит ли `$ time` что-либо после запуска` $ (find…) `. Вы должны получить эту ошибку, только если `$ time` пусто. slhck 10 лет назад 0
@Phil, что произойдет, если у вас есть каталоги, которые не содержат файлов. Поскольку значение `$ time` зависит от самого нового _file_ в каждом каталоге, оно будет пустым, если в каталогах нет файлов. Это не должно быть проблемой, просто игнорируйте эти сообщения, вам не нужны каталоги без файлов. terdon 10 лет назад 0

Похожие вопросы