测试文件名称是否在日期之后修改


11

我有一个文件名为YYYYMMDD的文件,例如

file-name-20151002.txt

我想确定文件在2015-10-02之后是否已修改。

笔记:

  • 我可以通过查看的输出来完成此操作ls,但是我知道解析输出ls是一个坏主意。
  • 我不需要查找在特定日期之后标出日期的所有文件,只需要一次测试一个特定文件。
  • 创建文件后,我不担心文件在同一日期被修改。也就是说,我只想知道20151002名称中带有此文件的是否在2015年10月3日或更晚时间被修改。
  • 我使用的是MacOs 10.9.5。

抱歉,不包括早期的操作系统。
Peter Grill

Answers:


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版本seddatestat

$ 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

这是未经测试的版本,但如果我正确阅读了在线手册页,则可以在Mac(和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

更好的解决方案是与POSIX相比不需要4-5扩展的解决方案。
Thomas Dickey

2
然后,随意编写自己的版本。我写的是我想写的东西,而不是你要我写的东西....包括使用GNU版本的工具。IMO,GNU 事实上的标准。以及相对于非Linux使用的Linux发行版的相对数量,非gnu * nixes支持。
cas

6
@cas:对Thomas的评论相当苛刻,我认为这只是改善您的解决方案的建议...在现实世界中,拥有“最标准的”脚本不仅是奖励,而且是必要的(或而是强制性的)。许多地方无法安装GNU版本的工具(我碰巧知道3个不同的地方,一些系统的最新bash版本为2.x,而awk是如此古老,非常接近原始版本)。这个问题的需求可能不是“关键的”(但可能是),也不是其他人经常寻求/使用的,但是总的来说,拥有便携式解决方案是+
Olivier Dulac

我正在sed: illegal option -- r使用MacOS 10.9.5。
Peter Grill

-r是一个GNU sed选项,告诉它使用扩展的正则表达式。您可以将sed脚本更改为使用基本的regexp而不是扩展的(例如,sed -e 's/^.*\(2015\)/\1/')但是脚本的其余部分可能仍然会失败,因为它需要dateand 的GNU版本stat,而且我不认为Mac是它们的标准配置(尽管它们是适用于Mac(预编译包))
cas

4

使用bash和statexpr将日期获取为数字并进行比较:

#!/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"。抱歉,我无法测试。
2015年

随着stat您的注释中的更改,事情似乎执行了,但是所有测试用例都没有输出。但是,这是"$file" =~ ([0-9]{8}).txt$怎么回事?
Peter Grill

它查找.txt文件名末尾的8位数字。括号()捕获8位数字,您可以在中找到它${BASH_REMATCH[1]}
2015年

如果您的bash if [[=~]]无法使用,可以尝试基本版本if filedate=$(expr "$file" : '.*-\([0-9]*\).txt'),然后替换${BASH_REMATCH[1]}$filedate
2015年

4

您可以这样做:

  • 将年/月/日值提取到外壳变量中,
  • 创建一个临时文件
  • 使用touch命令(为小时/分钟/秒添加0)来设置临时文件的修改日期

因为您的问题是关于bash,所以您可能正在使用Linux。在testLinux中使用的程序(的一部分的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用作临时文件的替代(比较OSXLinux):

#!/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命令删除被忽略的文件。
Thomas Dickey

我使用的是MacOS 10.9.5。
Peter Grill

感谢您的澄清。临时文件不像某些依赖其他扩展名的方法那样优雅,但是为了保持一致性,我将提供一个简短的示例脚本。
Thomas Dickey

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):具有glob限定符的递归glob。
  • [0-9](#c8)匹配8位数字。那是zsh的Extendedglob等效于ksh{8}([0-9])
  • D:包括隐藏文件
  • e@...@eval glob限定符,用于运行一些文本以进一步限定文件。传入的文件路径$REPLY
  • zstat...检索$m数组中格式为YYYYMMDD的mtime 。
  • ${REPLY:t}:扩展到文件的t尾(基本名称)。
  • ${(SM)something#pattern}。从某些东西中提取零件匹配模式。那些(小号 ubstring搜索)和(扩展到中号出动部分)被称为参数扩展标志SM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.