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.
Peter Grill
źródło
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
don_crissti
źródło
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
cas
źródło
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 ].

meuh
źródło
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
Thomas Dickey
źródło
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
Stéphane Chazelas
źródło