Melakukan tes -nt / -ot dalam POSIX sh

11

Built-in testdan [utilitas memiliki tes -nt("lebih baru dari") dan -ot("lebih tua dari") di sebagian besar shell, bahkan ketika shell berjalan dalam "mode POSIX" (juga berlaku untuk utilitas eksternal dengan nama yang sama pada sistem yang saya punya akses). Tes ini untuk membandingkan cap waktu modifikasi pada dua file. Semantik terdokumentasi mereka sedikit berbeda di seluruh implementasi (berkaitan dengan apa yang terjadi jika satu atau file lain ada pada tidak), tetapi mereka tidak termasuk dalam spesifikasi POSIX. untuk testutilitas .

Mereka tidak dibawa maju ke testutilitas ketika perintah kondisional dihapus dari shell [KornShell] karena mereka belum dimasukkan ke dalam testutilitas yang dibangun ke dalam implementasi historis shutilitas.

Dengan asumsi saya ingin membandingkan timestamp modifikasi antara file dalam /bin/shskrip shell dan kemudian mengambil tindakan tergantung pada apakah satu file lebih baru dari yang lain, seperti pada

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

... utilitas lain apa yang bisa saya gunakan, selain dari make(yang akan membuat sisa skrip sulit untuk sedikitnya)? Atau haruskah saya berasumsi bahwa tidak ada orang yang akan menjalankan skrip pada "implementasi historis sh", atau mengundurkan diri untuk menulis untuk shell tertentu seperti bash?

Kusalananda
sumber
Seperti yang Anda lihat dalam jawaban saya, bash tidak mengimplementasikan -ntfitur testdengan cara yang diharapkan sejak kira-kira. 1995. Anda harus menggunakan findekspresi basd bahkan bashjika Anda suka perilaku yang benar.
schily

Jawaban:

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

Menggunakan path absolut ke file mencegah false positive dengan nama file hanya berisi baris baru.

Jika menggunakan jalur relatif, maka ubah findperintah ke:

find -L "$f1" -prune -newer "$f2" -exec echo . \;
cuonglm
sumber
secara teknis, saya pikir itu gagal jika $f1hanya berisi baris baru;)
ilkkachu
1
@ilkkachu dapat dikerjakan dengan -exec echo x \;atau serupa?
muru
@uru, ya. -printfakan mudah jika standar
ilkkachu
3
@Kusalananda AFAICT, findstatus keluar tidak tergantung pada apa tes findpengembalian individu - kecuali mungkin jika ada kesalahan dalam benar-benar menjalankan tes tersebut. findkeluar 0 bahkan jika tidak ada file yang ditemukan.
muru
1
Perhatikan bahwa perilaku [ / -nt /nofile ]bervariasi dengan implementasi (tetapi tidak pernah menghasilkan kesalahan).
Stéphane Chazelas
7

Ini bisa menjadi kasus untuk menggunakan salah satu perintah Unix tertua ls,.

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

Hasilnya benar jika a lebih baru dari b.

meuh
sumber
3
Itu tidak berfungsi jika nama file dimulai dengan -(hilang --). Anda -Lharus setara dengan -nt. Itu tidak berfungsi untuk membandingkan xdan $'x\nx'misalnya.
Stéphane Chazelas
1
Eek! Tapi juga ya.
Kusalananda
4

Anda mengajukan pertanyaan menarik dan mengajukan klaim yang pertama-tama harus diverifikasi.

Saya memeriksa perilaku:

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

dengan berbagai kerang. Inilah hasilnya:

  • bash Tidak berfungsi - tidak mencetak apa pun.

  • Bosh berhasil

  • dash Tidak bekerja - tidak mencetak apa pun.

  • ksh88 Tidak berfungsi - tidak mencetak apa pun.

  • ksh93 bekerja

  • mksh Tidak berfungsi - tidak mencetak apa pun.

  • cetakan mewah : mewah: [: -nt: operator / operan yang tidak terduga

  • yash bekerja

  • zsh berfungsi di versi yang lebih baru , versi yang lebih lama tidak mencetak apa pun

Jadi empat dari sembilan shell mendukung fitur -nt dan mengimplementasikannya dengan benar. Dengan benar dalam hal ini berarti: dapat membandingkan perangko waktu pada platform terbaru yang mendukung rincian waktu perangko kedua . Perhatikan bahwa file yang saya pilih berbeda biasanya hanya beberapa mikrodetik dalam perangko waktunya.

Karena lebih mudah untuk menemukan findimplementasi yang berfungsi , saya sarankan untuk mengganti

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

oleh findekspresi berbasis.

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

berfungsi setidaknya selama $file1tidak hanya berisi baris baru.

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

sedikit lebih lambat tetapi berfungsi dengan benar.

BTW: Mengenai make saya tidak bisa berbicara untuk semua implementasi make, tetapi SunPro Makemendukung perbandingan waktu dengan granularity nanosecond sejak kira-kira. 20 tahun, sementara smakedan gmakemenambahkan fitur ini baru-baru ini.

schily
sumber
1
Apakah tes "tidak berfungsi" tidak berfungsi karena gagal membandingkan cap waktu sub-detik (pasti?), Atau yang lainnya? Berapa cap waktu aktual pada file yang terlibat dalam pengujian Anda? +1 untuk pengujian!
Kusalananda
2
Saya pikir downvote mungkin tentang kata-kata konfrontatif. Di sini, akan lebih adil untuk menyebutnya batasan (signifikan dalam beberapa konteks). Beberapa findimplementasi seperti busybox atau heirloom-toolchest akan memiliki batasan yang sama.
Stéphane Chazelas
3
Anda akan perlu -Luntuk yang findversi setara dengan -nt. Itu juga akan gagal pada nama file yang dimulai dengan -atau !, (...
Stéphane Chazelas
2
Sebagai soal fakta, saya sering mendapatkan downvote ketika saya menggunakan kata-kata konfrontatif. Di sini akan membantu jika Anda menyatakan konteksnya (file yang dimodifikasi dalam stempel waktu yang sama ketika dipotong ke resolusi kedua) pada awal jawaban.
Stéphane Chazelas
1
@sungguh, ya, BSD findpunya find -f "$file"untuk itu tetapi tidak portabel.
Stéphane Chazelas