私は一般的なコンパイル/翻訳システムを作成しています。ファイルが既にコンパイル/トランスパイルされているかどうかを確認する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、時間の細分性は秒よりも細かいです。できればもっと細かいものが必要です。
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秒未満の粒度をサポートしているわけではないことにも注意してください(bash
のtest
/_[
_ビルトインは、バージョン4.4以降、たとえばGNU test
およびtest
のzsh
ビルトインまたは_ksh93
_ do、少なくともGNU/Linux)。
参考のため:
test
ユーティリティの実装の場合(ただし、fish
またはBourneのようなシェルの場合、test
/_[
_ビルトインがあり、通常はそれを隠すため、代わりに_env test
_を使用します。 test
をバイパスする)、 get_mtime はtest.cで_struct timespec
_を読み取り、-nt
_ はそのデータを使用します組み込みでない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
この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
file1
はfile2
よりも(少し)新しいです。
ここでの問題は、時間値を正しく処理することです。
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
これは、file1
がfile2
よりも新しいことを示しています。
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つの変数に割り当て、それらを数値で比較します。
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 +ファイルシステムのファイル属性に保存することすらありません。