@ jw013 terima kasih! Jadi mungkin sesuatu seperti ln -s my.pid .lockakan mengklaim kunci (diikuti oleh echo $$ > my.pid) dan jika gagal dapat memeriksa apakah PID yang disimpan .lockadalah benar-benar contoh aktif dari skrip
Tobias Kienzler
Jawaban:
18
Hampir seperti jawaban nsg: gunakan direktori kunci . Pembuatan direktori adalah atom di linux dan unix dan * BSD dan banyak OS lainnya.
if mkdir $LOCKDIR
then# Do important, exclusive stuffif rmdir $LOCKDIR
then
echo "Victory is mine"else
echo "Could not remove lock dir">&2fielse# Handle error condition...fi
Anda dapat memasukkan PID dari sh penguncian ke dalam file di direktori kunci untuk tujuan debugging, tetapi jangan jatuh ke dalam jebakan berpikir Anda dapat memeriksa PID tersebut untuk melihat apakah proses penguncian masih dijalankan. Banyak kondisi lomba berada di jalur itu.
Saya akan mempertimbangkan menggunakan PID yang disimpan untuk memeriksa apakah instance penguncian masih hidup. Namun, inilah klaim yang mkdirtidak atom pada NFS (yang tidak berlaku untuk saya, tapi saya kira orang harus menyebutkan bahwa, jika benar)
Tobias Kienzler
Ya, tentu saja gunakan PID yang disimpan untuk melihat apakah proses penguncian masih dijalankan, tetapi jangan mencoba melakukan apa pun selain mencatat pesan. Pekerjaan memeriksa pid yang disimpan, membuat file PID baru, dll, meninggalkan jendela besar untuk balapan.
Bruce Ediger
Ok, seperti yang dinyatakan oleh Ihunath , lockdir kemungkinan besar berada di /tmpmana biasanya tidak dibagikan NFS, sehingga seharusnya tidak masalah.
Tobias Kienzler
Saya akan gunakan rm -rfuntuk menghapus direktori kunci. rmdirakan gagal jika seseorang (belum tentu Anda) berhasil menambahkan file ke direktori.
chepner
18
Untuk menambah jawaban Bruce Ediger , dan terinspirasi oleh jawaban ini , Anda juga harus menambahkan lebih banyak kecerdasan ke pembersihan untuk menjaga terhadap penghentian skrip:
#Remove the lock directoryfunction cleanup {if rmdir $LOCKDIR;then
echo "Finished"else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1fi}if mkdir $LOCKDIR;then#Ensure that if we "grabbed a lock", we release it#Works for SIGTERM and SIGINT(Ctrl-C)
trap "cleanup" EXIT
echo "Acquired lock, running"# Processing starts hereelse
echo "Could not create lock directory '$LOCKDIR'"
exit 1fi
Saya telah menggunakan kondisi ini selama seminggu, dan dalam 2 kesempatan itu tidak mencegah proses baru dari mulai. Saya pikir apa masalahnya - pid baru adalah substring yang lama dan disembunyikan oleh grep -v $$. contoh nyata: lama - 14532, baru - 1453, lama - 28858, baru - 858.
Naktibalda
Saya memperbaikinya dengan mengubah grep -v $$kegrep -v "^${$} "
Naktibalda
@Naktibalda tangkapan yang bagus, terima kasih! Anda juga dapat memperbaikinya dengan grep -wv "^$$"(lihat edit).
terdon
Terima kasih atas pembaruan itu. Pola saya terkadang gagal karena pids pendek dibiarkan penuh dengan spasi.
Naktibalda
4
Saya akan menggunakan file kunci, seperti yang disebutkan oleh Marco
(Saya pikir Anda lupa membuat file kunci) Bagaimana dengan kondisi balapan ?
Tobias Kienzler
ops :) Ya, kondisi lomba adalah masalah dalam contoh saya, saya biasanya menulis pekerjaan cron per jam atau harian dan kondisi ras jarang.
nsg
Mereka seharusnya tidak relevan dalam kasus saya juga, tetapi itu adalah sesuatu yang harus diingat. Mungkin menggunakan lsof $0itu tidak buruk juga?
Tobias Kienzler
Anda dapat mengurangi kondisi balapan dengan menuliskannya $$di file kunci. Kemudian sleepuntuk interval pendek dan membacanya kembali. Jika PID masih milik Anda, Anda berhasil mendapatkan kunci. Sama sekali tidak membutuhkan alat tambahan.
manatwork
1
Saya tidak pernah menggunakan lsof untuk tujuan ini, saya ini harus bekerja. Perhatikan bahwa lsof benar - benar lambat di sistem saya (1-2 detik) dan kemungkinan besar ada banyak waktu untuk kondisi balapan.
nsg
3
Jika Anda ingin memastikan bahwa hanya satu instance skrip Anda yang berjalan, lihat:
Kalau tidak, Anda dapat memeriksa psatau memohon lsof <full-path-of-your-script>, karena saya tidak akan menyebut mereka alat tambahan.
Suplemen :
sebenarnya saya berpikir untuk melakukannya seperti ini:
for LINE in`lsof -c <your_script> -F p`;doif[ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running"1>&2
exit 1;fidone
ini memastikan bahwa hanya proses dengan yang terendah pidtetap berjalan bahkan jika Anda melakukan fork-dan-exec beberapa contoh <your_script>secara bersamaan.
Terima kasih atas tautannya, tetapi bisakah Anda memasukkan bagian-bagian penting dalam jawaban Anda? Sudah menjadi kebijakan umum di SE untuk mencegah pembusukan tautan ... Tetapi sesuatu seperti [[(lsof $0 | wc -l) > 2]] && exitmungkin sebenarnya cukup, atau apakah ini juga rentan terhadap kondisi balapan?
Tobias Kienzler
Anda benar, bagian esensial dari jawaban saya tidak ada dan hanya memposting tautan yang cukup lemah. Saya menambahkan saran saya sendiri ke jawabannya.
user1146332
3
Satu cara lain untuk memastikan satu contoh skrip bash berjalan:
#!/bin/bash# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running"&& exit 1...
pidof -o %PPID -x $0 mendapatkan PID dari skrip yang ada jika sudah berjalan atau keluar dengan kode kesalahan 1 jika tidak ada skrip lain yang berjalan
Ini berasal dari bagian contoh man flock, yang selanjutnya menjelaskan:
Ini adalah kode boilerplate yang berguna untuk skrip shell. Letakkan di bagian atas skrip shell yang ingin Anda kunci dan itu akan secara otomatis mengunci sendiri pada proses pertama. Jika env var $ FLOCKER tidak disetel ke skrip shell yang sedang dijalankan, maka jalankan flock dan ambil kunci non-blocking eksklusif (menggunakan skrip itu sendiri sebagai file kunci) sebelum mengeksekusi kembali dengan argumen yang benar. Ini juga mengatur FLOCKER env var ke nilai yang benar sehingga tidak berjalan lagi.
Poin yang perlu dipertimbangkan:
Membutuhkan flock, skrip contoh diakhiri dengan kesalahan jika tidak dapat ditemukan
Ini adalah versi modifikasi dari Jawaban Anselmo . Idenya adalah membuat deskriptor file hanya baca menggunakan skrip bash itu sendiri dan gunakan flockuntuk menangani kunci.
SCRIPT=`realpath $0`# get absolute path to the script itself
exec 6<"$SCRIPT"# open bash script using file descriptor 6
flock -n 6||{ echo "ERROR: script is already running"&& exit 1;}# lock file descriptor 6 OR show error message if script is already running
echo "Run your single instance code here"
Perbedaan utama dari semua jawaban lain adalah bahwa kode ini tidak mengubah sistem file, menggunakan tapak yang sangat rendah dan tidak memerlukan pembersihan karena deskriptor file ditutup segera setelah skrip selesai terlepas dari status keluar. Jadi tidak masalah jika skrip gagal atau berhasil.
Anda harus selalu mengutip semua referensi variabel shell kecuali Anda memiliki alasan kuat untuk tidak melakukannya, dan Anda yakin tahu apa yang Anda lakukan. Jadi, Anda seharusnya melakukannya exec 6< "$SCRIPT".
Scott
@Scott Saya telah mengubah kode sesuai saran Anda. Terimakasih banyak.
John Doe
1
Saya menggunakan cksum untuk memeriksa skrip saya benar - benar menjalankan instance tunggal, bahkan saya mengubah nama file & path file .
Saya tidak menggunakan file jebakan & kunci, karena jika server saya tiba-tiba mati, saya harus menghapus file kunci secara manual setelah server naik.
Catatan: #! / Bin / bash di baris pertama diperlukan untuk grep ps
Atau Anda dapat melakukan hardcode cksum di dalam skrip Anda, sehingga Anda tidak perlu khawatir lagi jika Anda ingin mengubah nama file, path, atau konten skrip Anda .
Tolong jelaskan bagaimana hardcoding checksum adalah ide yang bagus.
Scott
bukan hardcoding checksum, ini hanya membuat kunci identitas skrip Anda, ketika instance lain akan berjalan, ia akan memeriksa proses skrip shell lain dan memasukkan file terlebih dahulu, jika kunci identitas Anda ada pada file itu, jadi itu berarti instance Anda sudah berjalan.
arputra
BAIK; harap edit jawaban Anda untuk menjelaskannya. Dan, di masa depan, jangan memposting beberapa blok kode sepanjang 30 baris yang terlihat seperti (hampir) identik tanpa mengatakan dan menjelaskan perbedaannya. Dan jangan katakan hal-hal seperti "Anda dapat melakukan hardcode [sic] cksum di dalam skrip Anda", dan jangan terus menggunakan nama variabel mysumdan fsum, ketika Anda tidak lagi berbicara tentang checksum.
Scott
Terlihat menarik, terima kasih! Dan selamat datang di unix.stackexchange :)
ln -s my.pid .lock
akan mengklaim kunci (diikuti olehecho $$ > my.pid
) dan jika gagal dapat memeriksa apakah PID yang disimpan.lock
adalah benar-benar contoh aktif dari skripJawaban:
Hampir seperti jawaban nsg: gunakan direktori kunci . Pembuatan direktori adalah atom di linux dan unix dan * BSD dan banyak OS lainnya.
Anda dapat memasukkan PID dari sh penguncian ke dalam file di direktori kunci untuk tujuan debugging, tetapi jangan jatuh ke dalam jebakan berpikir Anda dapat memeriksa PID tersebut untuk melihat apakah proses penguncian masih dijalankan. Banyak kondisi lomba berada di jalur itu.
sumber
mkdir
tidak atom pada NFS (yang tidak berlaku untuk saya, tapi saya kira orang harus menyebutkan bahwa, jika benar)/tmp
mana biasanya tidak dibagikan NFS, sehingga seharusnya tidak masalah.rm -rf
untuk menghapus direktori kunci.rmdir
akan gagal jika seseorang (belum tentu Anda) berhasil menambahkan file ke direktori.Untuk menambah jawaban Bruce Ediger , dan terinspirasi oleh jawaban ini , Anda juga harus menambahkan lebih banyak kecerdasan ke pembersihan untuk menjaga terhadap penghentian skrip:
sumber
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
,.Ini mungkin terlalu sederhana, tolong perbaiki saya jika saya salah. Bukankah ini
ps
cukup sederhana ?sumber
grep -v $$
. contoh nyata: lama - 14532, baru - 1453, lama - 28858, baru - 858.grep -v $$
kegrep -v "^${$} "
grep -wv "^$$"
(lihat edit).Saya akan menggunakan file kunci, seperti yang disebutkan oleh Marco
sumber
lsof $0
itu tidak buruk juga?$$
di file kunci. Kemudiansleep
untuk interval pendek dan membacanya kembali. Jika PID masih milik Anda, Anda berhasil mendapatkan kunci. Sama sekali tidak membutuhkan alat tambahan.Jika Anda ingin memastikan bahwa hanya satu instance skrip Anda yang berjalan, lihat:
Kunci skrip Anda (terhadap menjalankan paralel)
Kalau tidak, Anda dapat memeriksa
ps
atau memohonlsof <full-path-of-your-script>
, karena saya tidak akan menyebut mereka alat tambahan.Suplemen :
sebenarnya saya berpikir untuk melakukannya seperti ini:
ini memastikan bahwa hanya proses dengan yang terendah
pid
tetap berjalan bahkan jika Anda melakukan fork-dan-exec beberapa contoh<your_script>
secara bersamaan.sumber
[[(lsof $0 | wc -l) > 2]] && exit
mungkin sebenarnya cukup, atau apakah ini juga rentan terhadap kondisi balapan?Satu cara lain untuk memastikan satu contoh skrip bash berjalan:
pidof -o %PPID -x $0
mendapatkan PID dari skrip yang ada jika sudah berjalan atau keluar dengan kode kesalahan 1 jika tidak ada skrip lain yang berjalansumber
Meskipun Anda telah meminta solusi tanpa alat tambahan, ini adalah cara favorit saya menggunakan
flock
:Ini berasal dari bagian contoh
man flock
, yang selanjutnya menjelaskan:Poin yang perlu dipertimbangkan:
flock
, skrip contoh diakhiri dengan kesalahan jika tidak dapat ditemukanLihat juga /programming/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
sumber
Ini adalah versi modifikasi dari Jawaban Anselmo . Idenya adalah membuat deskriptor file hanya baca menggunakan skrip bash itu sendiri dan gunakan
flock
untuk menangani kunci.Perbedaan utama dari semua jawaban lain adalah bahwa kode ini tidak mengubah sistem file, menggunakan tapak yang sangat rendah dan tidak memerlukan pembersihan karena deskriptor file ditutup segera setelah skrip selesai terlepas dari status keluar. Jadi tidak masalah jika skrip gagal atau berhasil.
sumber
exec 6< "$SCRIPT"
.Saya menggunakan cksum untuk memeriksa skrip saya benar - benar menjalankan instance tunggal, bahkan saya mengubah nama file & path file .
Saya tidak menggunakan file jebakan & kunci, karena jika server saya tiba-tiba mati, saya harus menghapus file kunci secara manual setelah server naik.
Catatan: #! / Bin / bash di baris pertama diperlukan untuk grep ps
Atau Anda dapat melakukan hardcode cksum di dalam skrip Anda, sehingga Anda tidak perlu khawatir lagi jika Anda ingin mengubah nama file, path, atau konten skrip Anda .
sumber
mysum
danfsum
, ketika Anda tidak lagi berbicara tentang checksum.Anda dapat menggunakan ini: https://github.com/sayanarijit/pidlock
sumber
Kode saya untuk Anda
Berdasarkan
man flock
, hanya meningkatkan:executing
Di mana saya taruh di sini
sleep 10
, Anda bisa meletakkan semua skrip utama.sumber