POSIX shで-nt / -otテストを実行する


11

ビルトインtestおよび[ユーティリティは、シェルが「POSIXモード」で実行されている場合でも、ほとんどのシェルで-nt(「より新しい」)および-ot(「より古い」)テストを実行します(同じ名前の外部ユーティリティについても当てはまります)。私がアクセスできるシステム)。これらのテストは、2つのファイルの変更タイムスタンプを比較するためのものです。文書化されたセマンティクスは、実装ごとにわずかに異なります(一方または他方のファイルが存在しない場合に発生することに関して)が、POSIX仕様にtestは含まれいません以下のためのユーティリティ

test条件付きコマンドが[KornShell]シェルから削除されたとき、testユーティリティの歴史的な実装に組み込まれたユーティリティに含まれていないため、ユーティリティに引き継がれませんでしたsh

/bin/shシェルスクリプト内のファイル間の変更タイムスタンプを比較し、次に、1つのファイルが他のファイルより新しいかどうかに応じてアクションを実行したいと仮定します。

if [ "$sigfile"     -nt "$timestamp" ] ||
   [ "$sigfile.tmp" -nt "$timestamp" ]
then
    return
fi

... make(スクリプトの残りの部分は控えめに言っても扱いにくくなる)以外に、他にどのユーティリティを使用できますか?または、「歴史的な実装」でスクリプトを実行shしたり、特定のシェルの作成を辞めたりする人は誰もいないと仮定する必要がありbashますか?


あなたが私の答えを見ることができるように、bashは約の以来期待される方法での-nt機能を実装していませんtest。1995. 正しい動作が必要な場合findでも、basd式を使用する必要がありますbash
気味悪い

回答:


10

POSIXLY:

f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
    printf '%s is newer than %s\n' "$f1" "$f2"
fi

ファイルへの絶対パスを使用すると、ファイル名の誤検知を防止できます。改行のみが含まれます。

相対パスを使用する場合は、findコマンドを次のように変更します。

find -L "$f1" -prune -newer "$f2" -exec echo . \;

技術的には、$f1改行しか含まれていないと失敗します;)
ilkkachu

1
@ilkkachuは-exec echo x \;、同様の方法で回避できますか?
ムル

@muru、ええ。-printfそれが標準的だったら簡単でしょう
ilkkachu

3
@Kusalananda AFAICT、findの終了ステータスは、個々のテストのfind戻り値とは無関係です。ただし、実際にこれらのテストを実行するときにエラーが発生した場合は除きます。findファイルが見つからない場合でも0を終了します。
ムル

1
の動作は[ / -nt /nofile ]実装によって異なります(ただし、エラーは出力されません)。
ステファンChazelas

7

これは、最も古いUnixコマンドの1つを使用している場合である可能性がありlsます。

x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]

aがbよりも新しい場合、結果はtrueです。


3
ファイル名が-(missing --)で始まる場合は機能しません。-Lと同等である必要があり-ntます。それは比較することが動作しないx$'x\nx'、たとえば。
ステファンシャゼラス

1
イーク!しかし、ハァッ。
クサラナナンダ

4

興味深い質問を提起し、最初に確認する必要があるクレームを作成しました。

私は次の動作を確認しました:

$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'

様々な殻を持ちます。結果は次のとおりです。

  • bash機能しません-何も出力しません。

  • ボッシュ ワークス

  • ダッシュ機能しません-何も印刷しません。

  • ksh88は機能しません-何も出力しません。

  • ksh93 作品

  • mkshは機能しません-何も出力しません。

  • poshの出力:posh:[:-nt:予期しない演算子/オペランド

  • ヤッシュ ワークス

  • zsh は新しいバージョン動作し、古いバージョンでは何も印刷されません

したがって、9つのシェルのうち4つが-nt機能をサポートし、正しく実装します。この場合、正しくは、1秒未満のタイムスタンプの粒度をサポートする最近のプラットフォームでタイムスタンプを比較できることを意味します。私が選択したファイルは、通常、タイムスタンプが数マイクロ秒しか異なることに注意してください。

動作するfind実装を見つけるのが簡単なので、交換することをお勧めします

if [ "$file1" -nt "$file2" ] ; then
    echo newer
fi

findベースの式によって。

if [ "$( find "$file1" -newer "$file2" )" ]; then
    echo newer
fi

少なくとも$file1改行を含んでいない限りは機能します。

if [ "$( find -L "$file1" -newer "$file2" -exec echo newer \; )" ]; then
    echo newer
fi

少し遅くなりますが、正常に動作します。

ところで:makeについては、すべてのmake実装について話すことはできませんがSunPro Make、約1秒からのナノ秒粒度での時間比較をサポートしています。20年、しばらくsmakeしてgmake、最近、この機能を追加しました。


1
1秒未満のタイムスタンプの比較に失敗したために、「機能していない」テストは機能していませんか?テストに関係するファイルの実際のタイムスタンプは何ですか?テスト用+1!
クサラナンダ

2
反対投票はおそらく対立的な言葉遣いについてだと思います。ここでは、それを制限(状況によっては重要)と呼ぶ方が公平です。findbusyboxやheirloom-toolchestなどの一部の実装には同じ制限があります。
ステファンChazelas

3
あなたは、必要があると思い-Lためfindに相当するとバージョン-nt。また、ファイルで始まる名前に失敗し-たり!(...
ステファンChazelas

2
実際のところ、私が対立する言葉遣いを使用すると、しばしば反対票を獲得します。ここで、回答の最初にコンテキスト(2番目の解決に切り捨てられたときに同じタイムスタンプ内で変更されたファイル)を述べた場合に役立ちます。
ステファンChazelas

1
@schily、はい、BSD findfind -f "$file"はそれがありますが、移植性はありません。
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.