Проверить, был ли файл изменен после даты в имени файла

11

У меня есть файлы с именем YYYYMMDDв имени файла, например,

file-name-20151002.txt

Я хочу определить, был ли этот файл изменен после 2015-10-02.

Заметки:

  • Я могу сделать это, посмотрев на вывод ls, но я знаю, что разбор выходных данных ls- плохая идея.
  • Мне не нужно находить все файлы, датированные после определенной даты, просто нужно проверять один конкретный файл за раз.
  • Меня не беспокоит изменение файла в тот же день после его создания. То есть я просто хочу знать, был ли этот файл с 20151002именем изменен 03 октября 2015 года или позже.
  • Я на MacOs 10.9.5.
Питер Гриль
источник
Извиняюсь за то, что не включил ОС раньше.
Питер Гриль

Ответы:

4

Вот несколько возможных способов:

  • OSX stat:

    newer () {
    tstamp=${1:${#1}-12:8}
    mtime=$(stat -f "%Sm" -t "%Y%m%d" "$1")
    [[ ${mtime} -le ${tstamp} ]] && printf '%s\n' "$1 : NO: mtime is ${mtime}" || printf '%s\n' "$1 : YES: mtime is ${mtime}"
    }
  • GNU date:

    newer () {
    tstamp=${1:${#1}-12:8}
    mtime=$(date '+%Y%m%d' -r "$1")
    [[ ${mtime} -le ${tstamp} ]] && printf '%s\n' "$1 : NO: mtime is ${mtime}" || printf '%s\n' "$1 : YES: mtime is ${mtime}"
    }
  • zsh только:

    zmodload zsh/stat
    newer () {
    tstamp=${1:${#1}-12:8}
    mtime=$(zstat -F '%Y%m%d' +mtime -- $1)
    [[ ${mtime} -le ${tstamp} ]] && printf '%s\n' "$1 : NO: mtime is ${mtime}" || printf '%s\n' "$1 : YES: mtime is ${mtime}"
    }

Использование:

более новый файл

Пример вывода:

file-name-20150909.txt : YES: mtime is 20151026

или

file-name-20151126.txt : NO: mtime is 20151026
don_crissti
источник
9

Следующий скрипт проверит даты всех файлов, указанных в командной строке:

Требуются версии GNU sed, dateиstat

$ cat check-dates.sh 
#! /bin/bash

for f in "$@" ; do
  # get date portion of filename
  fdate=$(basename "$f" .txt | sed -re 's/^.*(2015)/\1/')

  # convert it to seconds since epoch + 1 day
  fsecs=$(echo $(date +%s -d "$fdate") + 86400 | bc )

  # get modified timestamp of file in secs since epoch
  ftimestamp=$(stat -c %Y "$f")

  [ "$ftimestamp" -gt "$fsecs" ] && echo "$f has been modified after $fdate"
done

$ ./check-dates.sh file-name-20151002.txt 
file-name-20151002.txt has been modified after 20151002
$ ls -l file-name-20151002.txt 
-rw-rw-r-- 1 cas cas 0 Oct 26 19:21 file-name-20151002.txt

Вот версия, которая не тестировалась, но должна работать на Mac (и на FreeBSD и т. Д.), Если я правильно прочитал справочные страницы в Интернете:

#! /bin/bash

for f in "$@" ; do
  # get date portion of filename
  fdate=$(basename "$f" .txt | sed -e 's/^.*\(2015\)/\1/')

  # convert it to seconds since epoch + 1 day
  fsecs=$(echo $(date -j -f %Y%m%d "$fdate" +%s) + 86400 | bc )

  # get modified timestamp of file in secs since epoch
  ftimestamp=$(stat -f %m "$f")

  [ "$ftimestamp" -gt "$fsecs" ] && echo "$f has been modified after $fdate"
done
саз
источник
Еще лучшим решением было бы решение, не требующее 4-5 расширений по сравнению с POSIX.
Томас Дики
2
Не стесняйтесь написать свою собственную версию. Я пишу то, что хочу написать, а не то, что вы хотите, чтобы я писал .... и это включает использование инструментов GNU-версий. ИМО, GNU IS стандартом де-факто. и относительное число используемых дистрибутивов Linux против не Linux, не-GNU * NIXES поддерживает это.
Cas
6
@cas: довольно резкий ответ на комментарий Томаса, который, как я полагаю, был просто предложением улучшить ваше решение ... В реальном мире наличие «максимально стандартного» сценария не только бонус, но и необходимо (или скорее, обязательно). Во многих местах не удается установить GNU-версию инструментов (я знаю 3 разных места, где последние версии bash для некоторых систем - 2.x, а awk настолько старый, что очень, очень близок к оригинальному). Потребности в этом вопросе, вероятно, не являются «критическими» (но могут быть), а также не часто ищутся / используются другими людьми, но в целом наличие портативного решения - это +
Olivier Dulac
Я получаю sed: illegal option -- rс MacOS 10.9.5.
Питер Гриль
-rявляется sedопцией GNU, чтобы сказать ей использовать расширенные регулярные выражения. Вы могли бы изменить сценарий sed, чтобы использовать базовое регулярное выражение, а не расширенное (например, sed -e 's/^.*\(2015\)/\1/')но остальная часть сценария, вероятно, все равно потерпит неудачу, потому что для этого требуются версии GNU dateи stat, и я не думаю, что Mac поставляются с ними как стандартные (хотя они для Mac в предварительно скомпилированных пакетов)
Cas
4

Используя bash и statи, exprчтобы получить даты в виде чисел и сравнить их:

#!/bin/bash
for file
do  moddate=$(stat -f %m -t %F "$file") # MacOS stat
    moddate=${moddate//-/} # 20151026
    if filedate=$(expr "$file" : '.*-\([0-9]*\).txt')
    then  if [ $moddate -gt $filedate ]
          then echo "$file: modified $moddate"
          fi
    fi
done

Это был мой предыдущий специфичный для Linux ответ.

#!/bin/bash
for file
do  moddate=$(stat -c %y "$file")
    moddate=${moddate%% *} # 2015-10-26
    moddate=${moddate//-/} # 20151026
    if [[ "$file" =~ ([0-9]{8}).txt$ ]]
    then  if [[ $moddate > ${BASH_REMATCH[1]} ]]
          then echo "$file: modified $moddate"
          fi
    fi
done

Оператор =~регулярного выражения bash записывает 8 цифр имени файла в массив bash BASH_REMATCH. [[ ]]сравнивает строки, хотя вы просто сравнить их как числа , а не в [ -gt ].

meuh
источник
MacOS 10.9.5, похоже, не имеет -cвозможности stat.
Питер Гриль
Виноват. Эквивалент , кажется stat -f %m -t %F "$file". Извините, я не могу это проверить.
meuh
С изменением в соответствии statс вашим комментарием вещи, кажется, выполняются, но не выводятся для всех тестовых случаев. Но что "$file" =~ ([0-9]{8}).txt$делать?
Питер Гриль
Он ищет 8 цифр, за которыми следует .txtконец имени файла. В скобках ()указана 8-значная часть, и вы можете найти ее в ${BASH_REMATCH[1]}.
meuh
Если ваш bash не работает с if [[=~]]вами, вы можете попробовать базовый, if filedate=$(expr "$file" : '.*-\([0-9]*\).txt')а затем заменить ${BASH_REMATCH[1]}на $filedate.
meuh
4

Вы могли бы сделать это:

  • извлечь значения года / месяца / дня в переменную оболочки,
  • создать временный файл
  • используйте touchкоманду (добавляя 0 для часа / минуты / секунды), чтобы установить дату изменения временного файла

Поскольку ваш вопрос о bash, вы, вероятно, используете Linux. testПрограмма , используемая в Linux (часть Coreutils ) имеет расширение для сравнения временных меток ( -ntи -ot) не найден в стандарте POSIXtest .

POSIX комментирует это в обосновании test:

Некоторые дополнительные основные цвета, недавно изобретенные или из KornShell, появились в раннем предложении как часть условной команды ( [[]]): s1> s2, s1 <s2, str = pattern, str! = Pattern, f1 -nt f2, f1 -ot f2, и f1 -ef f2. Они не были перенесены в тестовую утилиту, когда условная команда была удалена из оболочки, потому что они не были включены в тестовую утилиту, встроенную в исторические реализации утилиты sh.

Используя это расширение, вы можете затем

  • создайте другой временный файл с датой, с которой вы хотите сравнить
  • используйте -ntоператор в, testчтобы сделать сравнение, которое вы просили.

Вот пример. В пояснениях OP упоминается платформа, поэтому можно использовать ее statкак альтернативу временным файлам (сравните OSX и Linux ):

#!/bin/bash
# inspect each file...

with_tempfile() {
    echo "** with temporary file $name"
    test=$MYTEMP/$name
    touch -t $date $test
    [ $name -nt $test ] && echo "...newer"
}

# alternatively (and this is system-dependent)
with_stat() {
    echo "** with stat command $name"
    stat=$(stat -t "%Y%m%d%H%M" -f "%Sm" $name)
    [ $stat -gt $date ] && echo "...newer"
}

MYTEMP=$(mktemp -d /var/tmp/isnewer.XXXXXX)
trap "rm -rf $MYTEMP" EXIT
for name in file-name-[0-9][0-9]*.txt
do
    if [ -f "$name" ];
    then
            date=${name%%.txt}
            date=${date/file-name-}
            date=${date//-/}2359
            with_tempfile $name
            with_stat $name
    fi
done
Томас Дики
источник
При создании временных файлов обычной практикой является удаление пропущенных файлов с помощью trapкоманды.
Томас Дики
Я на MacOS 10.9.5.
Питер Гриль
Благодарю за разъяснение. Временные файлы не так элегантны, как некоторые возможные подходы, основанные на дополнительных расширениях, но я приведу краткий пример сценария для согласованности.
Томас Дики
1

Другой zshподход:

zmodload zsh/stat # best in ~/.zshrc or
zmodload -F zsh/stat +b:zstat # to avoid overriding an eventual
                              # system stat command.
setopt extendedglob # best in ~/.zshrc

ls -ld -- **/*[0-9](#c8)*(De@'zstat -F %Y%m%d -A m +mtime $REPLY &&
  [[ ${(SM)${REPLY:t}#[0-9](#c8)} != $m ]]'@)

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

zshимеет множество таких операторов для управления глобусами и переменными. Очень удобно выполнять любую работу быстро (и, как правило, надежно), но довольно сложно разобраться со всеми из них, и часто получается то, что мы обычно называем кодом только для записи (эвфемизм для неразборчивого кода, хотя также подразумевает, что вы не не нужно читать, как вы пишете для однократного использования в командной строке)

Быстрый пробег через:

  • **/pattern(qualifier): рекурсивный глобус с классификатором глобуса.
  • [0-9](#c8)соответствует 8 цифрам. Это zsh extendedglob-эквивалент ksh's {8}([0-9]).
  • D: включить скрытые файлы
  • e@...@: квалификатор eval glob для запуска текста для дальнейшего уточнения файлов. Путь к файлу передан в$REPLY
  • zstat...получить в массиве mtime в формате ГГГГММДД $m.
  • ${REPLY:t}: Расширяется до т хвоста (базового) файла.
  • ${(SM)something#pattern}, Извлеките образец соответствия детали из чего-либо . Те S( S поиск ubstring) и M(расширить до М atched части) называется флаги расширения параметров .
Стефан Шазелас
источник