web-dev-qa-db-ja.com

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

次のように、ファイル名にYYYYMMDDという名前のファイルがあります。

file-name-20151002.txt

thisファイルが2015-10-02以降に変更されたかどうかを確認したい。

ノート:

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

ここではいくつかの可能な方法があります:

  • 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
4
don_crissti

次のスクリプトは、コマンドラインで指定されたすべてのファイルの日付をチェックします。

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

これはテストされていないバージョンですが、オンラインのmanページを正しく読んだ場合、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
9
cas

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

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

あなたの質問はbashに関するものなので、おそらくLinuxを使用しています。 Linuxで使用される test プログラム( coreutils の一部)には、タイムスタンプ比較用の拡張機能(-ntおよび-ot)が見つかりません POSIX test

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

新しく発明された、またはKornShellから追加されたいくつかの追加のプライマリは、条件付きコマンド([[]])の一部として初期の提案に登場しました:s1> s2、s1 <s2、str =パターン、str! f1 -ot f2、およびf1 -ef f2。これらは、shユーティリティの歴史的な実装に組み込まれているテストユーティリティに含まれていないため、条件付きコマンドがシェルから削除されたときに、テストユーティリティに持ち越されませんでした。

その拡張機能を使用すると、次のことができます

  • 比較する日付を含む別の一時ファイルを作成します
  • test-nt演算子を使用して、要求した比較を行います。

例を示します。 OPによる説明でプラットフォームについて言及したため、一時ファイルの代わりにstatを使用できます(比較 [〜#〜] osx [〜#〜] および 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
4
Thomas Dickey

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 ]では文字列を数値として比較するだけです。

4
meuh

別の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には、グロブや変数を操作するための演算子がたくさんあります。すべての仕事をすばやく(そして一般的には確実に)実行するのは非常に便利ですが、それらすべてを理解するのは非常に難しく、通常はwrite-onlyコードと呼ばれるコードを生成します(判読不能なコードの冒涜、ただし、コマンドプロンプトで1回使用するために書き込むときに読み取る必要がないことも意味します)

簡単な概要:

  • **/pattern(qualifier):glob修飾子付きの再帰glob。
  • [0-9](#c8)は8桁に一致します。これは、ksh{8}([0-9])に相当するzsh extendedglobです。
  • D:隠しファイルを含める
  • _e@...@_:evalglob修飾子。テキストを実行してファイルをさらに修飾します。 _$REPLY_で渡されるファイルパス
  • _zstat..._ _$m_配列でYYYYMMDDとしてフォーマットされたmtimeを取得します。
  • _${REPLY:t}_:ファイルのt tail(ベース名)に展開されます。
  • ${(SM)something#pattern}somethingからpatternに一致する部分を抽出します。これらのS[〜#〜] s [〜#〜] ubstring search)およびM(expand to [〜#〜] m [ 〜#〜]キャッチされた部分)が呼び出されますパラメーター展開フラグ
1