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.

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

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

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 ].


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

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 .
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.