Перевірте, чи файл було змінено після дати у файлі Ім'я


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

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

Ось версія, яка не перевірена, але повинна працювати на Macs (і на 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. IMO, GNU - це стандарт де-факто. і відносна кількість дистрибутивів Linux у використанні проти нелінукс, не-gnu * nixes це підтримує.
cas

6
@cas: досить жорстка відповідь на коментар Томаса, який, я вважаю, була лише пропозицією покращити ваше рішення ... У реальному світі наявність "найстандартнішого можливого" сценарію - це не лише бонус, а необхідний (або скоріше, обов’язковий). У багатьох місцях не вдається встановити версію інструментів GNU (я, мабуть, знаю 3 різних місця, де остання система деяких версій bash становить 2.x, а awk настільки стара, що дуже, дуже близька до оригінальної). Потреби цього питання, ймовірно, не є "критичними" (але можуть бути), а також часто не шукаються / використовуються іншими людьми, але в цілому наявність портативного рішення - це +
Олів'є Дулак

Я отримую sed: illegal option -- rз MacOS 10.9.5.
Пітер Гриль

-r- це sedваріант GNU, щоб сказати йому використовувати розширені регулярні вирази. Ви можете змінити скрипт sed, щоб використовувати базовий регулярний вираз, а не розширений (наприклад, sed -e 's/^.*\(2015\)/\1/')але решта сценарію, ймовірно, все-таки вийде з ладу, оскільки для нього потрібні версії GNU dateі stat, і я не думаю, що Маки постачаються разом із ними як стандартні (хоча вони є доступний для 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 =~regexp фіксує 8 цифр імені файлу в масиві bash BASH_REMATCH. [[ ]]порівнює рядки, хоча ви просто порівнюєте їх як числа, а не в [ -gt ].


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 [[=~]]вами, ви можете спробувати basic, if filedate=$(expr "$file" : '.*-\([0-9]*\).txt')а потім замінити ${BASH_REMATCH[1]}на $filedate.
meuh

4

Ви можете це зробити:

  • витягнути значення року / місяця / дня в змінну оболонки,
  • створити тимчасовий файл
  • використовуйте touchкоманду (додаючи 0s на годину / хвилину / секунду), щоб встановити дату модифікації тимчасового файлу

Оскільки ваше питання про те 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 Exteggl еквівалент ksh's {8}([0-9]).
  • D: включати приховані файли
  • e@...@: класифікатор eval glob, щоб запустити текст для подальшої кваліфікації файлів. Шлях файлу пройшов$REPLY
  • zstat...отримати mtime, відформатований як YYYYMMDD в $mмасиві.
  • ${REPLY:t}: розширюється до t хвоста (основна назва) файлу.
  • ${(SM)something#pattern}. Витягніть шаблон, що відповідає частині, з чогось . Ті S( S ubstring search) та M(розширити до M- аттикованої частини) називаються прапорами розширення параметрів .
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.