Mengunci skrip shell yang benar?


66

Terkadang Anda harus memastikan bahwa hanya satu instance skrip shell yang berjalan pada saat yang sama.

Misalnya pekerjaan cron yang dijalankan melalui crond yang tidak menyediakan penguncian sendiri (misalnya default Solaris crond).

Pola umum untuk menerapkan penguncian adalah kode seperti ini:

#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then            # 'test' -> race begin
  echo Job is already running\!
  exit 6
fi
touch $LOCK                      # 'set'  -> race end
# do some work
rm $LOCK

Tentu saja, kode tersebut memiliki kondisi balapan. Ada jendela waktu di mana eksekusi dua instance dapat maju setelah baris 3 sebelum seseorang dapat menyentuh $LOCKfile.

Untuk pekerjaan cron ini biasanya tidak masalah karena Anda memiliki interval menit antara dua doa.

Tetapi ada yang salah - misalnya ketika lockfile ada di server NFS - yang hang. Dalam hal ini beberapa pekerjaan cron dapat memblokir pada baris 3 dan mengantri. Jika server NFS aktif lagi maka Anda memiliki guruh yang menjalankan pekerjaan paralel.

Mencari di web saya menemukan alat lockrun yang sepertinya solusi yang baik untuk masalah itu. Dengannya Anda menjalankan skrip yang perlu dikunci seperti ini:

$ lockrun --lockfile=/var/tmp/mylock myscript.sh

Anda bisa memasukkan ini ke dalam pembungkus atau menggunakannya dari crontab Anda.

Menggunakan lockf()(POSIX) jika tersedia dan kembali ke flock()(BSD). Dan lockf()dukungan terhadap NFS harus relatif luas.

Apakah ada alternatif lain lockrun?

Bagaimana dengan daemon cron lainnya? Apakah ada crond umum yang mendukung penguncian dengan cara yang waras? Pandangan cepat ke halaman manual Vixie Crond (default pada sistem Debian / Ubuntu) tidak menunjukkan apa pun tentang penguncian.

Apakah ide yang bagus untuk memasukkan alat seperti lockrunke dalam coreutils ?

Menurut pendapat saya itu menerapkan tema yang sangat mirip dengan timeout, nicedan teman-teman.


4
Intinya, dan untuk kepentingan orang lain yang mungkin menganggap pola awal Anda Cukup Baik (tm), kode shell itu mungkin harus menjebak TERM untuk menghapus pengunciannya saat killed; dan tampaknya merupakan praktik yang baik untuk menyimpan pid Anda sendiri di lockfile, daripada hanya menyentuhnya.
Ulrich Schwarz


@ Shawn, tidak juga, tidak menyebutkan crond dan NFS.
maxschlepzig


1
@Ulrich sangat terlambat, menyimpan PID di NFS lockfile menambah nilai yang sangat kecil. Bahkan menambahkan nama host masih tidak benar-benar membantu untuk memeriksa proses langsung
roaima

Jawaban:


45

Berikut cara lain untuk mengunci skrip shell yang dapat mencegah kondisi balapan yang Anda jelaskan di atas, di mana dua pekerjaan dapat melewati baris 3. noclobberOpsi akan bekerja dalam ksh dan bash. Jangan gunakan set noclobberkarena Anda tidak boleh menulis skrip di csh / tcsh. ;)

lockfile=/var/tmp/mylock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then

        trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT

        # do stuff here

        # clean up after yourself, and release your trap
        rm -f "$lockfile"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi

YMMV dengan mengunci NFS (Anda tahu, ketika server NFS tidak dapat dijangkau), tetapi secara umum jauh lebih kuat daripada dulu. (10 tahun yang lalu)

Jika Anda memiliki pekerjaan cron yang melakukan hal yang sama pada waktu yang sama, dari beberapa server, tetapi Anda hanya perlu 1 instance untuk benar-benar dijalankan, sesuatu seperti ini mungkin bekerja untuk Anda.

Saya tidak punya pengalaman dengan lockrun, tetapi memiliki lingkungan kunci yang telah ditentukan sebelum script benar-benar berjalan mungkin membantu. Atau mungkin juga tidak. Anda hanya mengatur tes untuk lockfile di luar skrip Anda dalam pembungkus, dan secara teoritis, tidak bisakah Anda menekan kondisi balapan yang sama jika dua pekerjaan dipanggil oleh lockrun pada waktu yang bersamaan, seperti halnya dengan 'inside- solusi the-script '?

Penguncian file cukup banyak untuk menghormati perilaku sistem, dan setiap skrip yang tidak memeriksa keberadaan lockfile sebelum menjalankan akan melakukan apa pun yang akan mereka lakukan. Hanya dengan memasukkan tes lockfile, dan perilaku yang tepat, Anda akan menyelesaikan 99% masalah potensial, jika tidak 100%.

Jika Anda sering mengalami kondisi perlombaan lockfile, ini mungkin merupakan indikator masalah yang lebih besar, seperti tidak memiliki waktu yang tepat untuk pekerjaan Anda, atau mungkin jika intervalnya tidak sepenting penyelesaian pekerjaan, mungkin pekerjaan Anda lebih cocok untuk dihidupkan kembali. .


EDIT DI BAWAH - 2016-05-06 (jika Anda menggunakan KSH88)


Berdasarkan komentar @Clint Pachl di bawah ini, jika Anda menggunakan ksh88, gunakan mkdirsebagai ganti noclobber. Ini sebagian besar mengurangi kondisi ras potensial, tetapi tidak sepenuhnya membatasi itu (meskipun risikonya sangat kecil). Untuk informasi lebih lanjut baca tautan yang diposting Clint di bawah ini .

lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid

if ( mkdir ${lockdir} ) 2> /dev/null; then
        echo $$ > $pidfile
        trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
        # do stuff here

        # clean up after yourself, and release your trap
        rm -rf "$lockdir"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi

Dan, sebagai keuntungan tambahan, jika Anda perlu membuat tmpfile di skrip Anda, Anda dapat menggunakan lockdirdirektori untuk mereka, mengetahui bahwa mereka akan dibersihkan ketika skrip keluar.

Untuk bash yang lebih modern, metode noclobber di bagian atas harus sesuai.


1
Tidak, dengan lockrun Anda tidak memiliki masalah - ketika server NFS hang, semua panggilan lockrun akan hang (setidaknya) dalam lockf()system call - ketika dicadangkan semua proses dilanjutkan tetapi hanya satu proses yang akan memenangkan kunci. Tidak ada kondisi balapan. Saya tidak mengalami masalah seperti itu dengan cronjobs banyak - yang terjadi sebaliknya - tetapi ini adalah masalah ketika hits Anda memiliki potensi untuk membuat banyak rasa sakit.
maxschlepzig

1
Saya telah menerima jawaban ini karena metode ini aman dan sejauh ini yang paling elegan. Saya menyarankan varian kecil: set -o noclobber && echo "$$" > "$lockfile"untuk mendapatkan fallback yang aman ketika shell tidak mendukung opsi noclobber.
maxschlepzig

3
Jawaban yang bagus, tetapi Anda juga harus 'membunuh -0' nilai di lockfile untuk memastikan bahwa proses yang menciptakan kunci masih ada.
Nigel Horne

1
The noclobberpilihan mungkin rentan terhadap kondisi ras. Lihat mywiki.wooledge.org/BashFAQ/045 untuk beberapa makanan untuk dipikirkan.
Clint Pachl

2
Catatan: menggunakan noclobber(atau -C) di ksh88 tidak berfungsi karena ksh88 tidak digunakan O_EXCLuntuk noclobber. Jika Anda menjalankan dengan shell yang lebih baru, Anda mungkin baik-baik saja ...
jrw32982

14

Saya lebih suka menggunakan tautan keras.

lockfile=/var/lock/mylock
tmpfile=${lockfile}.$$
echo $$ > $tmpfile
if ln $tmpfile $lockfile 2>&-; then
    echo locked
else
    echo locked by $(<$lockfile)
    rm $tmpfile
    exit
fi
trap "rm ${tmpfile} ${lockfile}" 0 1 2 3 15
# do what you need to

Hard link adalah atom lebih dari NFS dan untuk sebagian besar, mkdir juga . Menggunakan mkdir(2)atau link(2)hampir sama, pada tingkat praktis; Saya hanya lebih suka menggunakan tautan keras karena lebih banyak implementasi NFS yang memungkinkan tautan keras atom daripada atom mkdir. Dengan rilis NFS modern, Anda tidak perlu khawatir menggunakan keduanya.


12

Saya mengerti itu mkdiratomik, jadi mungkin:

lockdir=/var/tmp/myapp
if mkdir $lockdir; then
  # this is a new instance, store the pid
  echo $$ > $lockdir/PID
else
  echo Job is already running, pid $(<$lockdir/PID) >&2
  exit 6
fi

# then set traps to cleanup upon script termination 
# ref http://www.shelldorado.com/goodcoding/tempfiles.html
trap 'rm -r "$lockdir" >/dev/null 2>&1' 0
trap "exit 2" 1 2 3 13 15

Ok, tapi saya tidak dapat menemukan informasi apakah mkdir()lebih dari NFS (> = 3) distandarisasi untuk menjadi atom.
maxschlepzig

2
@maxschlepzig RFC 1813 tidak secara eksplisit memanggil untuk mkdirmenjadi atomik (memang untuk rename). Dalam praktiknya, diketahui bahwa beberapa implementasi tidak. Terkait: utas menarik, termasuk kontribusi oleh penulis GNU arch .
Gilles 'SANGAT berhenti menjadi jahat'

8

Cara mudah adalah dengan menggunakan paket yang lockfilebiasanya datang procmail.

LOCKFILE="/tmp/mylockfile.lock"
# try once to get the lock else exit
lockfile -r 0 "$LOCKFILE" || exit 0

# here the actual job

rm -f "$LOCKFILE"

5

semyang datang sebagai bagian dari parallelalat GNU mungkin apa yang Anda cari:

sem [--fg] [--id <id>] [--semaphoretimeout <secs>] [-j <num>] [--wait] command

Seperti dalam:

sem --id my_semaphore --fg "echo 1 ; date ; sleep 3" &
sem --id my_semaphore --fg "echo 2 ; date ; sleep 3" &
sem --id my_semaphore --fg "echo 3 ; date ; sleep 3" &

keluaran:

1
Thu 10 Nov 00:26:21 UTC 2016
2
Thu 10 Nov 00:26:24 UTC 2016
3
Thu 10 Nov 00:26:28 UTC 2016

Perhatikan bahwa pesanan tidak dijamin. Juga output tidak ditampilkan sampai selesai (menjengkelkan!). Namun demikian, ini adalah cara paling ringkas yang saya tahu untuk menjaga dari eksekusi bersamaan, tanpa khawatir tentang lockfile dan coba lagi dan pembersihan.


Apakah penguncian yang ditawarkan oleh sempegangan ditembak jatuh di tengah eksekusi?
roaima

2

Saya menggunakan dtach.

$ dtach -n /tmp/socket long_running_task ; echo $?
0
$ dtach -n /tmp/socket long_running_task ; echo $?
dtach: /tmp/socket: Address already in use
1

1

Saya menggunakan alat baris perintah "kawanan" untuk mengelola kunci di skrip bash saya, seperti yang dijelaskan di sini dan di sini . Saya telah menggunakan metode sederhana ini dari halaman kawanan, untuk menjalankan beberapa perintah dalam sebuah subkulit ...

   (
     flock -n 9
     # ... commands executed under lock ...
   ) 9>/var/lock/mylockfile

Dalam contoh itu, gagal dengan kode keluar 1 jika tidak dapat memperoleh file kunci. Tetapi flock juga dapat digunakan dengan cara yang tidak membutuhkan perintah untuk dijalankan di sub-shell :-)


3
The flock()system call tidak bekerja lebih NFS .
maxschlepzig

1
BSD memiliki alat serupa, "lockf".
dubiousjim

2
@dubiousjim, BSD lockf juga memanggil flock()dan bermasalah dengan NFS. Btw, sementara itu, flock () di Linux sekarang kembali ke fcntl()ketika file tersebut terletak di mount NFS, dengan demikian, dalam lingkungan NFS hanya Linux flock()sekarang bekerja lebih dari NFS.
maxschlepzig

1

Jangan gunakan file.

Jika skrip Anda dieksekusi seperti ini misalnya:

bash my_script

Anda dapat mendeteksi apakah itu berjalan menggunakan:

running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do 
  echo Already locked
  exit 6
fi

Hm, kode pemeriksaan ps dijalankan dari dalam my_script? Dalam hal ini contoh lain sedang berjalan - tidak running_procmengandung dua baris yang cocok? Saya suka idenya, tetapi tentu saja - Anda akan mendapatkan hasil yang salah ketika pengguna lain menjalankan skrip dengan nama yang sama ...
maxschlepzig

3
Ini juga termasuk kondisi lomba: jika 2 instance mengeksekusi baris pertama secara paralel maka tidak ada yang mendapatkan 'kunci' dan keduanya keluar dengan status 6. Ini akan menjadi semacam kelaparan satu putaran timbal balik . Btw, saya tidak yakin mengapa Anda menggunakan, $!bukan $$dalam contoh Anda.
maxschlepzig

@maxschlepzig memang menyesal tentang $ yang salah! vs. $$
frogstarr78

@maxschlepzig untuk menangani banyak pengguna yang menjalankan skrip, tambahkan euser = ke argumen -o.
frogstarr78

@maxschlepzig untuk mencegah beberapa baris, Anda juga dapat mengubah argumen menjadi grep, atau "filter" tambahan (mis grep -v $$.). Pada dasarnya saya berusaha memberikan pendekatan yang berbeda untuk masalah tersebut.
frogstarr78

1

Untuk penggunaan aktual, Anda harus menggunakan jawaban terpilih teratas .

Namun, saya ingin membahas beberapa pendekatan yang rusak dan semi-bisa diterapkan menggunakan psdan banyak peringatan yang mereka miliki, karena saya terus melihat orang menggunakannya.

Jawaban ini benar-benar jawaban untuk "Mengapa tidak menggunakan psdan grepmenangani penguncian di shell?"

Pendekatan yang rusak # 1

Pertama, pendekatan yang diberikan dalam jawaban lain yang memiliki beberapa upvotes terlepas dari kenyataan bahwa itu tidak (dan tidak pernah bisa) bekerja dan jelas tidak pernah diuji:

running_proc=$(ps -C bash -o pid=,cmd= | grep my_script);
if [[ "$running_proc" != "$$ bash my_script" ]]; do 
  echo Already locked
  exit 6
fi

Mari kita perbaiki kesalahan sintaks dan psargumen yang rusak dan dapatkan:

running_proc=$(ps -C bash -o pid,cmd | grep "$0");
echo "$running_proc"
if [[ "$running_proc" != "$$ bash $0" ]]; then
  echo Already locked
  exit 6
fi

Script ini akan selalu keluar 6, setiap kali, tidak peduli bagaimana Anda menjalankannya.

Jika Anda menjalankannya ./myscript, maka pshasilnya hanya akan 12345 -bash, yang tidak cocok dengan string yang diperlukan 12345 bash ./myscript, sehingga akan gagal.

Jika Anda menjalankannya bash myscript, segalanya menjadi lebih menarik. Proses bash bercabang untuk menjalankan pipa, dan shell anak menjalankan psdan grep. Shell asli dan shell anak akan muncul di psoutput, kira-kira seperti ini:

25793 bash myscript
25795 bash myscript

Itu bukan hasil yang diharapkan $$ bash $0, jadi skrip Anda akan keluar.

Pendekatan yang rusak # 2

Sekarang dalam keadilan bagi pengguna yang menulis pendekatan # 1 yang rusak, saya melakukan sesuatu yang serupa saat pertama kali mencoba ini:

if otherpids="$(pgrep -f "$0" | grep -vFx "$$")" ; then
  echo >&2 "There are other copies of the script running; exiting."
  ps >&2 -fq "${otherpids//$'\n'/ }" # -q takes about a tenth the time as -p
  exit 1
fi

Ini hampir berhasil. Tetapi fakta forking untuk menjalankan pipa membuang ini. Jadi yang ini akan selalu keluar juga.

Pendekatan tidak dapat diandalkan # 3

pids_this_script="$(pgrep -f "$0")"
if not_this_process="$(echo "$pids_this_script" | grep -vFx "$$")"; then
  echo >&2 "There are other copies of this script running; exiting."
  ps -fq "${not_this_process//$'\n'/ }"
  exit 1
fi

Versi ini menghindari masalah forking pipa dalam pendekatan # 2 dengan terlebih dahulu mendapatkan semua PID yang memiliki skrip saat ini dalam argumen baris perintah mereka, dan kemudian memfilter pidlist itu, secara terpisah, untuk menghilangkan PID dari skrip saat ini.

Ini mungkin berhasil ... asalkan tidak ada proses lain yang memiliki baris perintah yang cocok dengan $0, dan menyediakan skrip selalu disebut dengan cara yang sama (misalnya jika itu disebut dengan jalur relatif dan kemudian jalur absolut, contoh terakhir tidak akan melihat yang sebelumnya ).

Pendekatan tidak dapat diandalkan # 4

Jadi bagaimana jika kita lewati memeriksa baris perintah penuh, karena itu mungkin tidak menunjukkan skrip benar-benar berjalan, dan lsofbukannya memeriksa untuk menemukan semua proses yang memiliki skrip ini terbuka?

Ya, pendekatan ini sebenarnya tidak terlalu buruk:

if otherpids="$(lsof -t "$0" | grep -vFx "$$")"; then
  echo >&2 "Error: There are other processes that have this script open - most likely other copies of the script running.  Exiting to avoid conflicts."
  ps >&2 -fq "${otherpids//$'\n'/ }"
  exit 1
fi

Tentu saja, jika salinan skrip sedang berjalan, maka instance baru akan mulai baik-baik saja dan Anda akan menjalankan dua salinan .

Atau jika skrip yang berjalan dimodifikasi (mis. Dengan Vim atau dengan a git checkout), maka skrip versi "baru" akan mulai tanpa masalah, karena keduanya Vim dan git checkoutmenghasilkan file baru (inode baru) menggantikan yang lama.

Namun, jika skrip tidak pernah dimodifikasi dan tidak pernah disalin, maka versi ini cukup bagus. Tidak ada kondisi balapan karena file skrip harus dibuka sebelum pemeriksaan dapat dicapai.

Masih bisa ada false positive jika proses lain membuka file skrip, tetapi perhatikan bahwa meskipun itu terbuka untuk diedit di Vim, vim tidak benar-benar menahan file skrip terbuka sehingga tidak akan menghasilkan false positive.

Tapi ingat, jangan gunakan pendekatan ini jika skrip mungkin diedit atau disalin karena Anda akan mendapatkan negatif palsu, yaitu beberapa kejadian berjalan sekaligus - jadi fakta bahwa mengedit dengan Vim tidak memberikan hasil positif palsu, seharusnya tidak menjadi masalah. kepadamu. Saya menyebutkannya, karena pendekatan # 3 memang memberikan positif palsu (yaitu menolak untuk memulai) jika Anda memiliki skrip terbuka dengan Vim.

Lalu apa yang harus dilakukan?

Jawaban teratas untuk pertanyaan ini memberikan pendekatan yang bagus dan solid.

Mungkin Anda bisa menulis yang lebih baik ... tetapi jika Anda tidak memahami semua masalah dan peringatan dengan semua pendekatan di atas, Anda tidak akan menulis metode penguncian yang menghindari semuanya.



0

Inilah sesuatu yang kadang-kadang saya tambahkan di server untuk dengan mudah menangani kondisi lomba untuk pekerjaan apa pun di mesin. Ini serupa dengan pos Tim Kennedy, tetapi dengan cara ini Anda mendapatkan penanganan balapan dengan hanya menambahkan satu baris ke setiap skrip bash yang membutuhkannya.

Letakkan konten di bawah ini di mis / opt / racechecker / racechecker:

ZPROGRAMNAME=$(readlink -f $0)
EZPROGRAMNAME=`echo $ZPROGRAMNAME | sed 's/\//_/g'`
EZMAIL="/usr/bin/mail"
EZCAT="/bin/cat"

if  [ -n "$EZPROGRAMNAME" ] ;then
        EZPIDFILE=/tmp/$EZPROGRAMNAME.pid
        if [ -e "$EZPIDFILE" ] ;then
                EZPID=$($EZCAT $EZPIDFILE)
                echo "" | $EZMAIL -s "$ZPROGRAMNAME already running with pid $EZPID"  alarms@someemail.com >>/dev/null
                exit -1
        fi
        echo $$ >> $EZPIDFILE
        function finish {
          rm  $EZPIDFILE
        }
        trap finish EXIT
fi

Inilah cara menggunakannya. Perhatikan baris setelah shebang:

     #/bin/bash
     . /opt/racechecker/racechecker
     echo "script are running"
     sleep 120

Cara kerjanya adalah ia mengetahui nama file bashscript utama dan membuat pidfile di bawah "/ tmp". Ini juga menambahkan pendengar pada sinyal akhir. Pendengar akan menghapus pidfile ketika skrip utama selesai dengan benar.

Sebaliknya jika pidfile ada ketika sebuah instance diluncurkan, maka pernyataan if yang berisi kode di dalam if-statement kedua akan dieksekusi. Dalam hal ini saya telah memutuskan untuk meluncurkan surat alarm ketika ini terjadi.

Bagaimana jika skrip mogok

Latihan selanjutnya adalah menangani tabrakan. Idealnya pidfile harus dihapus bahkan jika skrip utama macet karena alasan apa pun, ini tidak dilakukan dalam versi saya di atas. Itu berarti jika skrip macet, pidfile harus dihapus secara manual untuk mengembalikan fungsionalitas.

Dalam kasus sistem crash

Ini adalah ide yang baik untuk menyimpan pidfile / lockfile di bawah sebagai contoh / tmp. Dengan cara ini skrip Anda akan terus dieksekusi setelah sistem crash karena pidfile akan selalu dihapus saat bootup.


Tidak seperti ansatz Tim Kennedy, skrip Anda TIDAK mengandung kondisi ras. Ini karena Anda memeriksa keberadaan PIDFILE dan pembuatan kondisionalnya tidak dilakukan dalam operasi atom.
maxschlepzig

Beri +1 pada itu! Saya akan mempertimbangkan ini dan memodifikasi skrip saya.
ziggestardust

-2

Periksa skrip saya ...

Anda mungkin MENYUKAINYA ....

[rambabu@Server01 ~]$ sh Prevent_cron-OR-Script_against_parallel_run.sh
Parallel RUN Enabled
Now running
Task completed in Parallel RUN...
[rambabu@Server01 ~]$ cat Prevent_cron-OR-Script_against_parallel_run.sh
#!/bin/bash
#Created by RambabuKella
#Date : 12-12-2013

#LOCK file name
Parallel_RUN="yes"
#Parallel_RUN="no"
PS_GREP=0
LOCK=/var/tmp/mylock_`whoami`_"$0"
#Checking for the process
PS_GREP=`ps -ef |grep "sh $0" |grep -v grep|wc -l`
if [ "$Parallel_RUN" == "no" ] ;then
echo "Parallel RUN Disabled"

 if [ -f $LOCK ] || [ $PS_GREP -gt 2   ] ;then
        echo -e "\nJob is already running OR LOCK file exists. "
        echo -e "\nDetail are : "
        ps -ef |grep  "$0" |grep -v grep
        cat "$LOCK"
  exit 6
 fi
echo -e "LOCK file \" $LOCK \" created on : `date +%F-%H-%M` ." &> $LOCK
# do some work
echo "Now running"
echo "Task completed on with single RUN ..."
#done

rm -v $LOCK 2>/dev/null
exit 0
else

echo "Parallel RUN Enabled"

# do some work
echo "Now running"
echo "Task completed in Parallel RUN..."
#done

exit 0
fi
echo "some thing wrong"
exit 2
[rambabu@Server01 ~]$

-3

Saya menawarkan solusi berikut ini, dalam sebuah skrip bernama 'flocktest'

#!/bin/bash
export LOGFILE=`basename $0`.logfile
logit () {
echo "$1" >>$LOGFILE
}
PROGPATH=$0
(
flock -x -n 257
(($?)) && logit "'$PROGPATH' is already running!" && exit 0
logit "'$PROGPATH', proc($$): sleeping 30 seconds"
sleep 30
)257<$PROGPATH
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.