Teste se o arquivo foi modificado após a data no nome do arquivo

11

Eu tenho um arquivo nomeado com YYYYMMDDno nome do arquivo, como

file-name-20151002.txt

Desejo determinar se este arquivo foi modificado após 02/10/2015.

Notas:

  • Eu posso fazer isso olhando a saída de ls, mas sei que analisar a saída de lsé uma má idéia.
  • Não preciso encontrar todos os arquivos datados após uma data específica, só preciso testar um arquivo específico por vez.
  • Não estou preocupado com o arquivo ser modificado na mesma data após a criação. Ou seja, eu só quero saber se este arquivo com 20151002o nome foi modificado em 03 de outubro de 2015 ou posterior.
  • Estou no MacOs 10.9.5.
Peter Grill
fonte
Desculpas por não incluir o SO anteriormente.
Peter Grill

Respostas:

4

Aqui estão algumas maneiras possíveis com:

  • 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 só:

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

Uso:

FILE mais recente

Exemplo de saída:

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

ou

file-name-20151126.txt : NO: mtime is 20151026
don_crissti
fonte
9

O script a seguir verificará as datas de todos os arquivos especificados na linha de comando:

Ele requer versões GNU de sed, dateestat

$ 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

Aqui está uma versão que não foi testada, mas deve funcionar em Macs (e no FreeBSD etc.) se eu li as páginas de manual on-line corretamente:

#! /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
fonte
Uma solução ainda melhor seria não exigir 4-5 extensões vs POSIX.
Thomas Dickey
2
Sinta-se livre para escrever sua própria versão, então. Eu escrevo o que quero escrever, não o que você quer que eu escreva ... e isso inclui o uso de versões de ferramentas GNU. IMO, GNU é o padrão de fato. e o número relativo de distribuições linux em uso versus non-linux, non-gnu * nixes suporta isso.
26515
6
@cas: uma resposta bastante dura ao comentário de Thomas, que, acredito, foi apenas uma sugestão para melhorar sua solução ... No mundo real, ter o script "o mais padrão possível" não é apenas um bônus, mas é necessário (ou obrigatório). Muitos lugares não conseguem instalar a versão GNU das ferramentas (eu conheço três lugares diferentes, nos quais o verso mais recente do bash de alguns sistemas é 2.xe o awk é tão antigo que é muito, muito próximo do original). As necessidades desta pergunta provavelmente não são "críticas" (mas poderiam ser) e também não são frequentemente procuradas / usadas por outras pessoas, mas, em geral, ter uma solução portátil é um +
Olivier Dulac
Estou recebendo sed: illegal option -- rcom o MacOS 10.9.5.
Peter Grill
-ré uma sedopção GNU para dizer a ele para usar expressões regulares estendidas. Você pode alterar o script sed para usar regexp básico em vez de estendido (por exemplo, sed -e 's/^.*\(2015\)/\1/')mas o restante do script provavelmente ainda falhará porque requer as versões GNU de datee stat, e eu não acho que os Macs os acompanhem como padrão (embora sejam disponível para Mac em pacotes pré-compilados)
cas
4

Usando bash e state exprobter as datas como números e compará-los:

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

Esta foi minha resposta específica para Linux anterior.

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

O =~operador bash regexp captura os 8 dígitos do nome do arquivo no array bash BASH_REMATCH. [[ ]]compara as strings, embora você simplesmente as compare como números em vez disso [ -gt ].

meuh
fonte
O MacOS 10.9.5 parece não ter a -copção stat.
Peter Grill
Foi mal. O equivalente parece ser stat -f %m -t %F "$file". Desculpe, não posso testá-lo.
Meu
Com a alteração no statseu comentário, as coisas parecem executar, mas não há saída para todos os casos de teste. Mas o que está "$file" =~ ([0-9]{8}).txt$fazendo?
Peter Grill
Ele procura os 8 dígitos seguidos por .txtno final do nome do arquivo. Os parênteses ()capturam a parte de 8 dígitos e você pode encontrá-la ${BASH_REMATCH[1]}.
Meu
Se o seu bash não funcionar, if [[=~]]tente o básico if filedate=$(expr "$file" : '.*-\([0-9]*\).txt')e substitua-o ${BASH_REMATCH[1]}por $filedate.
meuh 29/10/2015
4

Você poderia fazer isso:

  • extrair os valores ano / mês / dia em uma variável shell,
  • crie um arquivo temporário
  • use o touchcomando (adicionando 0s por hora / minuto / segundo) para definir a data de modificação do arquivo temporário

Porque sua pergunta é sobre bash, você provavelmente está usando Linux. O testprograma usado no Linux (parte do coreutils ) possui extensões para comparação de timestamp ( -nte -ot) não encontradas no POSIXtest .

O POSIX comenta sobre isso na justificativa para test:

Algumas primárias adicionais recentemente inventadas ou do KornShell apareceram em uma proposta inicial como parte do comando condicional ( [[]]): s1> s2, s1 <s2, str = padrão, str! = Padrão, f1 -nt f2, f1 -ot f2, e f1 -ef f2. Eles não foram transportados para o utilitário de teste quando o comando condicional foi removido do shell porque não foram incluídos no utilitário de teste incorporado às implementações históricas do utilitário sh.

Usando essa extensão, você pode então

  • crie outro arquivo temporário com a data em que você deseja comparar
  • use o -ntoperador testpara fazer a comparação solicitada.

Aqui está um exemplo. Um esclarecimento do OP mencionou a plataforma, para que se pudesse usar statcomo alternativa aos arquivos temporários (compare OSX e 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
fonte
Ao criar arquivos temporários, é prática padrão remover arquivos ignorados com um trapcomando.
Thomas Dickey
Estou no MacOS 10.9.5.
Peter Grill
Obrigado pelo esclarecimento. Os arquivos temporários não são tão elegantes quanto algumas abordagens possíveis, contando com extensões adicionais, mas fornecerei um pequeno exemplo de script, para obter consistência.
21815 Thomas Dickey
1

Outra zshabordagem:

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

Relataria os arquivos que possuem algo que parece um carimbo de data / hora em seu nome, mas que não corresponde ao horário da última modificação.

zshpossui uma infinidade de operadores como esse para manipular globs e variáveis. É muito útil realizar qualquer trabalho rapidamente (e geralmente de maneira confiável), mas é muito difícil entender tudo isso e geralmente produz o que geralmente chamamos de código somente de gravação (eufemismo para código ilegível, mas também implica que você não você não precisa lê-lo enquanto o escreve para um único uso no prompt de comando)

Uma rápida análise:

  • **/pattern(qualifier): glob recursivo com qualificador glob.
  • [0-9](#c8)corresponde a 8 dígitos. Esse é o equivalente do zsh extendedglob de ksh's {8}([0-9]).
  • D: incluir arquivos ocultos
  • e@...@: o qualificador eval glob para executar algum texto para qualificar ainda mais os arquivos. Caminho do arquivo passado$REPLY
  • zstat...recupere o mtime formatado como AAAAMMDD na $mmatriz.
  • ${REPLY:t}: expande para a cauda t (o nome da base) do arquivo.
  • ${(SM)something#pattern}. Extraia o padrão de correspondência de peças de algo . Aqueles S( S pesquisa ubstring) e M(expandir para M atched parte) são chamados de bandeiras de expansão parâmetro .
Stéphane Chazelas
fonte