web-dev-qa-db-ja.com

2つのファイル変更日を比較する

私は一般的なコンパイル/翻訳システムを作成しています。ファイルが既にコンパイル/トランスパイルされているかどうかを確認する1つの方法は、ソースファイルとターゲットファイルの変更日を比較することです。

それを実行できるbashスクリプトを作成する必要があります。

source_file=foo;
target_file=bar;

stat_source=$(stat source_file);
stat_target=$(stat target_file);

しかし、どうすれば統計出力から日付を抽出して比較できますか?ファイルの最新の変更時刻を比較するためのstatより良い方法はありますか?

ログファイルでstatを呼び出すと、次のようになります。

16777220 12391188 -rw-r--r-- 1 alexamil staff 0 321 "Jun 22 17:45:53 2017" "Jun 22 17:20:51 2017" "Jun 22 17:20:51 2017" "Jun 22 15:40:19 2017" 4096 8 0 test.log

AFAICT、時間の細分性は秒よりも細かいです。できればもっと細かいものが必要です。

8
Alexander Mills

stat(機能は似ていますが、BSDとGNUでは出力形式が異なります)を使用している場合、この比較を直接行うtestユーティリティを使用することもできます。

_   FILE1 -nt FILE2
          FILE1 is newer (modification date) than FILE2

   FILE1 -ot FILE2
          FILE1 is older than FILE2
_

あなたの例では、

_if [ "$source_file" -nt "$target_file" ]
then
    printf '%s\n' "$source_file is newer than $target_file"
fi
_

この機能はPOSIXでは使用できません(その test)のドキュメントを参照 )。これは根拠として提供されます。

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

ただし、将来的には変更される可能性があります この機能は広くサポートされています。

オペランドがシンボリックリンクである場合、考慮されるのはシンボリックリンクのターゲットの変更時間です(通常、これは必要なことです。そうでない場合は、代わりに_find -newer_を使用してください)。シンボリックリンクを解決できない場合、実装間の動作(既存のファイルを解決できないものより常に新しいと見なすものもあれば、オペランドのいずれかができる場合は常にfalseを報告するものもあります解決されません)。

また、すべての実装が1秒未満の粒度をサポートしているわけではないことにも注意してください(bashtest/_[_ビルトインは、バージョン4.4以降、たとえばGNU testおよびtestzshビルトインまたは_ksh93_ do、少なくともGNU/Linux)。

参考のため:

  • GNU testユーティリティの実装の場合(ただし、fishまたはBourneのようなシェルの場合、test/_[_ビルトインがあり、通常はそれを隠すため、代わりに_env test_を使用します。 testをバイパスする)、 get_mtime はtest.cで_struct timespec_を読み取り、
  • オプション_-nt_ はそのデータを使用します
16
Thomas Dickey

組み込みでないLinuxを想定する場合は、test外部コマンドを使用できます。これは、GNU coreutilsの一部です。(testは、[の別の名前です。ほとんどのシェルに組み込まれています。ナノ秒の粒度(ファイルシステムによって報告される精度まで)を持っています。

/usr/bin/test "$target" -nt "$source"

-nt演算子はPOSIXでは定義されていませんが、ダッシュ、bash、pdksh、mksh、ATT ksh、zsh、GNU coreutils test、BusyBoxなどの多くの実装に存在します。ただし、多くの実装(ダッシュ、bash、pdksh、mksh、BusyBox — Debian jessieでテスト済み)では、1秒の粒度しかサポートされていません。

しかし make など、このジョブ専用のツールを使用することをお勧めします。特定のファイルが他のファイルよりも新しい場合にのみコマンドを実行することが目的です。 Makefileというファイルに次の内容を含めます(eachoコマンドラインの前にタブ文字が必要です)。

target: source
    echo This command is only executed if target is newer than source
    do_stuff <source >$@

make targetを実行して、それを生成するコマンドを実行します。 targetが存在し、sourceより新しい場合、コマンドは実行されません。詳細については、makeのドキュメントを読んでください。

GNU日付は、ファイルのmtimeをエポック以降のナノ秒にフォーマットできます。

date +%s%N --reference file

これらの数値は、testまたは '['と同等です。 i386(32ビット)システム(2008年頃)のbash v3.00でも動作します。

xxx=$(date +%s%N --reference file1)
yyy=$(date +%s%N --reference file2)
[ $xxx -lt $yyy ] && echo file1 is older than file2
1
jmccanta

このLinuxシステムでのテスト。ファイル時間をテストする通常の方法はシェルです:

[ file1 -nt file2 ] && echo "yes"

秒で動作するようです。これは、1秒未満の時間差でファイルにアクセスしますが、その差は検出されません。

$ touch file2; sleep 0.1; touch file1; [ file1 -nt file2 ] && echo "yes"

問題を確認するには(ドットがナノ秒後の時間):

$ ls --time-style=full-iso -l file?
-rw-r--r-- 1 user user 0 2017-06-23 01:37:01.707387495 -0400 file1
-rw-r--r-- 1 user user 0 2017-06-23 01:37:01.599392538 -0400 file2

file1file2よりも(少し)新しいです。

ここでの問題は、時間値を正しく処理することです。

1つの解決策は、lsのフォーマットされた出力を使用することです。

$ ls --time-style=+%s.%N -l file?
-rw-r--r-- 1 user user 0 1498196221.707387495 file1
-rw-r--r-- 1 user user 0 1498196221.599392538 file2

2つの変数への時間の抽出(ドットなし):

$ file1time=$(ls --time-style=+%s%N -l file1 | awk "{print(\$6)}")
$ file2time=$(ls --time-style=+%s%N -l file2 | awk "{print(\$6)}")

時間を比較します(ナノ秒の時間は64ビット値にほとんど適合しません。システムが64ビットを使用していない場合、この比較は失敗します)。

$ [ $file1time -gt $file2time ] && echo "yes"
yes

これは、file1file2よりも新しいことを示しています。


lsが必要な形式になっていない場合は、statを試してください。

$ stat file1
  File: file1
  Size: 0               Blocks: 0          IO Block: 4096   regular file
Device: 805h/2053d      Inode: 9180838     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/    user)   Gid: ( 1000/    user)
Access: 2017-06-23 01:37:01.707387495 -0400
Modify: 2017-06-23 01:37:01.707387495 -0400
Change: 2017-06-23 01:37:01.707387495 -0400
 Birth: -

出力がナノ秒を示している場合、時刻を解析(およびフォーマット)するには日付が必要です。

$ stat --printf='%y\n' file1
2017-06-23 01:37:01.707387495 -0400

$ date +'%s%N' -d "$(stat --printf='%y\n' file1)" 
1498196221707387495

残りは同じで、file1とfile2の結果を2つの変数に割り当て、それらを数値で比較します。

1
Isaac

POSIXlyでは、findを使用します。

if find "$source_file" -Prune -newer "$target_file" | grep -q '^'; then
  printf '%s\n' "$source_file is newer than $target_file"
else
  echo "It's not newer or one of the files is not accessible"
fi

シンボリックリンクの場合、それはシンボリックリンク自体のmtimeを比較します。シンボリックリンクのターゲットを比較するには、-Hまたは-Lオプションを追加します。

これは、$source_file-で始まっておらず、それ以外の場合はfind述部の1つに対応していないことを前提としています。任意のファイル名を処理する必要がある場合は、最初に次のようなことを行う必要があります。

case $source_file in
  (["-+()!"]*) source_file=./$source_file;;
esac

GNUおよびFreeBSDのfindの実装では、少なくとも1秒未満の細分性がサポートされています。 AFAICT、macosは少なくとも1秒未満の時間情報をHFS +ファイルシステムのファイル属性に保存することすらありません。

0