Sprawdź, czy plik został zmodyfikowany po dacie w nazwie pliku


11

Mam pliki o nazwie z YYYYMMDDnazwą pliku, takie jak

file-name-20151002.txt

Chcę ustalić, czy ten plik został zmodyfikowany po 02.10.2015.

Uwagi:

  • Mogę to zrobić, patrząc na wynik ls, ale wiem, że analizowanie wyniku lsjest złym pomysłem.
  • Nie muszę znajdować wszystkich plików datowanych po określonej dacie, wystarczy przetestować jeden konkretny plik naraz.
  • Nie martwię się, że plik zostanie zmodyfikowany w tym samym dniu po jego utworzeniu. To znaczy, chcę tylko wiedzieć, czy ten plik z 20151002nazwą w nazwie został zmodyfikowany 3 października 2015 roku lub później.
  • Jestem na MacO 10.9.5.

Przepraszamy za wcześniejsze włączenie systemu operacyjnego.
— Peter Grill

Odpowiedzi:


4

Oto kilka możliwych sposobów:

  • 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 tylko:

    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}"
    }
    

Stosowanie:

nowszy PLIK

Przykładowe dane wyjściowe:

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

lub

file-name-20151126.txt : NO: mtime is 20151026

9

Poniższy skrypt sprawdzi daty wszystkich plików określonych w wierszu poleceń:

Wymaga to wersje z GNU sed, dateorazstat

$ 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

Oto wersja, która nie została przetestowana, ale powinna działać na komputerach Mac (i FreeBSD itp.), Jeśli poprawnie przeczytałem strony podręcznika online:

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

Jeszcze lepszym rozwiązaniem byłoby takie, które nie wymaga 4-5 rozszerzeń w porównaniu z POSIX.
— Thomas Dickey,

2
Napisz więc własną wersję. Piszę to, co chcę napisać, a nie to, co chcę, żebym napisał .... a to obejmuje używanie wersji GNU narzędzi. IMO, GNU JEST de facto standardem. i względna liczba używanych linuksowych dystrybucji w porównaniu z nie-linuxowymi, nie-gnu * nixami to obsługuje.
— cas

6
@cas: dość ostra odpowiedź na komentarz Thomasa, który, jak sądzę, była tylko sugestią ulepszenia twojego rozwiązania ... W prawdziwym świecie posiadanie „najbardziej standardowego” skryptu jest nie tylko premią, ale jest konieczne (lub raczej obowiązkowe). Wiele miejsc nie może zainstalować narzędzi GNU (znam 3 różne miejsca, w których najnowsze wersje bash niektórych systemów to 2.x, a awk jest tak stary, że jest bardzo, bardzo zbliżony do oryginalnego). Potrzeby tego pytania prawdopodobnie nie są „krytyczne” (ale mogą być), a także nie są często poszukiwane / wykorzystywane przez innych ludzi, ale ogólnie rzecz biorąc, posiadanie przenośnego rozwiązania jest +
— Olivier Dulac

Dostaję sed: illegal option -- rz MacOS 10.9.5.
— Peter Grill

-rjest sedopcją GNU , która nakazuje używać rozszerzonych wyrażeń regularnych. Możesz zmienić skrypt sed, aby używał podstawowego wyrażenia regularnego zamiast rozszerzonego (np. sed -e 's/^.*\(2015\)/\1/')Ale reszta skryptu prawdopodobnie nadal zawiedzie, ponieważ wymaga wersji GNU datei stat, i nie sądzę, że Mac są dostarczane z nimi jako standard (chociaż są dostępne dla komputerów Mac we wstępnie skompilowanych pakietach)
— cas

4

Używając bash i statoraz, expraby uzyskać daty jako liczby i porównać je:

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

To była moja wcześniejsza odpowiedź związana z Linuksem.

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

Operator wyrażenia =~regularnego bash przechwytuje 8 cyfr nazwy pliku do tablicy bash BASH_REMATCH. [[ ]]porównuje ciągi znaków, chociaż zamiast tego porównuje się je jako liczby [ -gt ].


MacOS 10.9.5 nie wydaje się mieć takiej -cmożliwości stat.
— Peter Grill

Mój błąd. Wydaje się, że to odpowiednik stat -f %m -t %F "$file". Przepraszam, nie mogę tego przetestować.
— Meuh

Po zmianie parametru w statkomentarzu wydaje się, że wszystko działa , ale nie ma danych wyjściowych dla wszystkich przypadków testowych. Ale co się "$file" =~ ([0-9]{8}).txt$dzieje?
— Peter Grill

Szuka 8 cyfr, a następnie .txtna końcu nazwy pliku. Nawiasy ()przechwytują 8 cyfr i możesz je znaleźć ${BASH_REMATCH[1]}.
— Meuh

Jeśli bash praca robi z if [[=~]]was może spróbować podstawową if filedate=$(expr "$file" : '.*-\([0-9]*\).txt'), a następnie zastąpić ${BASH_REMATCH[1]}przez $filedate.
— Meuh

4

Możesz to zrobić:

  • wyodrębnij wartości roku / miesiąca / dnia do zmiennej powłoki,
  • utwórz plik tymczasowy
  • użyj touchpolecenia (dodając 0 dla godziny / minuty / sekundy), aby ustawić datę modyfikacji pliku tymczasowego

Ponieważ twoje pytanie dotyczy bash, prawdopodobnie używasz Linuksa. testProgram używany w systemie Linux (część coreutils ) posiada rozszerzenia dla porównania timestamp ( -nti -ot) nie została odnaleziona w POSIXtest .

Komentarze POSIX na ten temat w uzasadnieniu dla test:

Niektóre dodatkowe elementy podstawowe nowo wynalezione lub z KornShell pojawiły się we wczesnej propozycji jako część polecenia warunkowego ( [[]]): s1> s2, s1 <s2, str = wzorzec, str! = Wzorzec, f1 -nt f2, f1 -ot f2, i f1 -ef f2. Nie zostały przeniesione do narzędzia testowego, gdy polecenie warunkowe zostało usunięte z powłoki, ponieważ nie zostały uwzględnione w narzędziu testowym wbudowanym w historyczne implementacje narzędzia sh.

Korzystając z tego rozszerzenia, możesz wtedy

  • utwórz kolejny plik tymczasowy z datą, z którą chcesz porównać
  • użyj -ntoperatora, testaby dokonać porównania, o które prosiłeś.

Oto przykład. Wyjaśnienie OP wspomniało o platformie, więc można ją wykorzystać statjako alternatywę dla plików tymczasowych (porównaj OSX i 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

Podczas tworzenia plików tymczasowych standardową praktyką jest usuwanie przeoczonych plików za pomocą trappolecenia.
— Thomas Dickey,

Jestem na MacOS 10.9.5.
— Peter Grill

Dziękuję za wyjaśnienie. Pliki tymczasowe nie są tak eleganckie, jak niektóre możliwe podejścia oparte na dodatkowych rozszerzeniach, ale przedstawię krótki przykładowy skrypt dla zachowania spójności.
— Thomas Dickey,

1

Inne zshpodejście:

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 ]]'@)

Zgłasza pliki, które mają w nazwie coś, co wygląda jak znacznik czasu, ale nie odpowiada czasowi ostatniej modyfikacji.

zshma mnóstwo takich operatorów do manipulowania globami i zmiennymi. Bardzo przydatne jest szybkie wykonanie dowolnej pracy (i ogólnie niezawodnie), ale ciężko jest obejść je wszystkie i często wytwarza to, co zwykle nazywamy kodem tylko do zapisu (eufemizm w przypadku nieczytelnego kodu, choć oznacza również, że nie T trzeba go przeczytać podczas pisania do pojedynczego użycia w wierszu polecenia)

Szybki przegląd:

  • **/pattern(qualifier): glob rekurencyjny z kwalifikatorem glob.
  • [0-9](#c8)dopasowuje 8 cyfr. To zsh extendedglob odpowiednikiem ksh„s {8}([0-9]).
  • D: dołącz ukryte pliki
  • e@...@: kwalifikator eval glob do uruchomienia tekstu w celu dalszej kwalifikacji plików. Przekazano ścieżkę pliku$REPLY
  • zstat...pobierz w tablicy mtime sformatowany jako RRRRMMDD $m.
  • ${REPLY:t}: rozwija się do ogona t (basename) pliku.
  • ${(SM)something#pattern}. Wyodrębnij z czegoś wzór pasujący do części . Te ( S szukanie ciągu) i (rozwinięcie do części M ) są nazywane flagami rozszerzania parametrów .SM
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.