ファイル名の日付より後にファイルが変更されたかどうかをテストする


11

次のように、ファイル名にYYYYMMDDで名前が付けられたファイルがあります。

file-name-20151002.txt

このファイルが2015-10-02以降に変更されたかどうかを確認したいと思います。

ノート:

  • の出力を確認することでこれを行うことができますlsが、の出力を解析することlsは悪い考えです。
  • 特定の日付以降のすべてのファイルを見つける必要はありません。一度に1つの特定のファイルをテストするだけです。
  • 作成後、同じ日にファイルが変更される心配はありません。つまり、この20151002名前のファイルが2015年10月3日以降に変更されたかどうかを知りたいだけです。
  • MacOs 10.9.5を使用しています。

以前にOSを含めなかったことをお詫びします。
Peter Grill、

回答:


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のバージョンを必要としseddate、およびstat

$ 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はISのデファクトスタンダード。使用中のLinuxディストリビューションとLinux以外、GNU以外の* nixesの相対的な数がこれをサポートしています。
cas

6
@cas:トーマスのコメントに対するかなり厳しい返答は、私が思うに、あなたの解決策を改善するための単なる提案でした...現実の世界では、「最も標準的な」スクリプトを使用することは、ボーナスだけでなく、必要です(またはむしろ、必須)。多くの場所では、GNUバージョンのツールをインストールできません(たまたま、システムの最新バージョンのbashが2.xであり、awkが非常に古いため、元の場所に非常に近い3つの異なる場所を知っています)。この質問のニーズはおそらく「重要」ではなく(しかし可能性がある)、他の人が頻繁に求めたり使用したりすることもありませんが、一般に、ポータブルソリューションは+
Olivier Dulac

私は取得していますsed: illegal option -- rMacOSの10.9.5で。
Peter Grill、

-rsed拡張正規表現を使用するように指示するGNU オプションです。あなたは、たとえば、(基本的な正規表現を使用するのではなく、拡張するsedスクリプトを変更することができますsed -e 's/^.*\(2015\)/\1/')が、それはGNUのバージョンを必要とするため、スクリプトの残りの部分はおそらくまだで失敗しますdatestat、彼らはしているが、私は、(標準としてMacは彼らと一緒に来るとは思いませんMacのプリコンパイル済みパッケージで利用可能)
cas

4

bashおよびstatand exprを使用して日付を数値として取得し、それらを比較します。

#!/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
Peter Grill、

私の悪い。同等のようstat -f %m -t %F "$file"です。テストできません。
meuh

statコメントに従ってas が変更されたため、すべてのテストケースで出力は実行されないように見えます。しかし、何をし"$file" =~ ([0-9]{8}).txt$ていますか?
Peter Grill

.txtファイル名の最後にある8桁を検索します。括弧()は8桁の部分を表しており、で見つけることができます${BASH_REMATCH[1]}
meuh

使用して、bashのdoesntの仕事ならばif [[=~]]、あなたは基本的に試すことができますif filedate=$(expr "$file" : '.*-\([0-9]*\).txt')し、次に置き換える${BASH_REMATCH[1]}ことで$filedate
meuh

4

あなたはこれを行うことができます:

  • 年/月/日の値をシェル変数に抽出し、
  • 一時ファイルを作成する
  • 使用touch一時ファイルの更新日時を設定する(時/分/秒0を追加)コマンドを

あなたの質問はについてなので、bashおそらくLinuxを使用しています。testLinuxの中で使用されたプログラム(の一部coreutilsのは)タイムスタンプ比較用の拡張機能を持っている(-nt-ot)には見られないPOSIXtest

POSIXはこれについての根拠としてこれについてコメントしていますtest

新しく発明された、またはKornShellから追加されたいくつかのプライマリは、条件付きコマンド([[]])の一部として初期の提案に登場しました:s1> s2、s1 <s2、str =パターン、str!および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グロブと変数を操作するためにそのような演算子がたくさんあります。すべての仕事をすばやく(そして一般的には確実に)完了するのは非常に便利ですが、それらすべてを理解することは非常に難しく、通常は書き込み専用コードと呼ばれるものを生成することがよくあります(判読不能なコードの推論ですが、そうしないことも意味します) tコマンドプロンプトで1回使用するために書き込むときに読み取る必要があります)

簡単な概要:

  • **/pattern(qualifier):グロブ修飾子を持つ再帰グロブ。
  • [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}何かから部分一致パターンを抽出します。これらSS ubstring search)およびM(Expand to M atched part)は、パラメーター拡張フラグと呼ばれます
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.