Bir kerede yalnızca bir kabuk betiği örneğinin çalıştığından emin olmanın hızlı ve kirli yolu


Yanıtlar:


109

İşte bir lockfile kullanan ve içine bir PID yansıtan bir uygulama . Bu, pidfile kaldırılmadan önce işlem öldürülürse bir koruma görevi görür :

LOCKFILE=/tmp/lock.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
    echo "already running"
    exit
fi

# make sure the lockfile is removed when we exit and then claim it
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $$ > ${LOCKFILE}

# do stuff
sleep 1000

rm -f ${LOCKFILE}

Buradaki hile, kill -0sinyal vermeyen, ancak verilen PID ile bir işlem olup olmadığını kontrol eder. Ayrıca çağrı çağrısı , işleminiz öldürüldüğünde bile kilit dosyasının kaldırılmasını trapsağlayacaktır (hariç ).kill -9


73
Anter cevabı üzerine bir yorumda daha önce belirtildiği gibi, bunun ölümcül bir kusuru var - diğer komut dosyası kontrol ile eko arasında başlarsa, tost edersiniz.
Paul Tomblin

1
Symlink hile düzgün, ancak lockfile sahibi öldürmek -9'd ise veya sistem çökerse, symlink'i okumak, sahibinin gittiğini fark etmek ve sonra silmek için hala bir yarış koşulu vardır. Çözümüme sadık kalıyorum.
bmdhacks

9
Atomik kontrol ve oluşturma işlemi, sürü (1) veya kilit dosyası (1) kullanılarak kabukta kullanılabilir. Diğer cevaplara bakın.
dmckee --- eski moderatör yavru kedi

3
Flock veya lockfile gibi yardımcı programlara güvenmek zorunda kalmadan atomik bir kontrol yapmanın ve oluşturmanın taşınabilir bir yolu için cevabımı görün.
lhunath

2
Bu atomik değildir ve bu nedenle işe yaramaz. Test ve set için atomik bir mekanizmaya ihtiyacınız var.
K Richard Pixley

214

flock(1)Dosya tanımlayıcıda özel bir kapsam kilidi yapmak için kullanın . Bu şekilde komut dosyasının farklı bölümlerini de senkronize edebilirsiniz.

#!/bin/bash

(
  # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
  flock -x -w 10 200 || exit 1

  # Do stuff

) 200>/var/lock/.myscript.exclusivelock

Bu olmasını sağlar kod arasındaki o (ve) bir seferde bir işlemle sadece çalıştırılır ve süreç çok uzun bir kilit beklemek gelmez.

Uyarı: bu özel komut bir parçasıdır util-linux. Linux dışında bir işletim sistemi çalıştırırsanız, kullanılabilir olabilir veya olmayabilir.


11
200 nedir? Manulda "fd" yazıyor, ama bunun ne anlama geldiğini bilmiyorum.
chovy

4
@chovy "dosya tanımlayıcı", açık bir dosya belirten bir tamsayı tanıtıcısı.
Alex B

6
Başka biri merak ederse: Sözdizimi ( command A ) command Biçin bir alt kabuk çağırır command A. Tldp.org/LDP/abs/html/subshells.html adresinde belgelenmiştir .
Alt

1
Ben alt kabuk içindeki kod daha gibi olması gerektiğini düşünüyorum: if flock -x -w 10 200; then ...Do stuff...; else echo "Failed to lock file" 1>&2; fizaman aşımı oluşursa (başka bir işlem dosya kilitli), bu komut dosyası devam ve dosyayı değiştirmek yok. Muhtemelen ... karşıt argüman '10 saniye sürdü ve kilit hala mevcut değilse, asla mevcut olmayacaktır', muhtemelen kilidi tutan işlem sona ermediğinden (belki de çalıştırılıyor olabilir) hata ayıklayıcı altında?).
Jonathan Leffler

1
Yönlendirilen dosya yalnızca kilidin çalışması için bir yer klasörüdür, içine anlamlı veri girilmez. İçindeki exitkısımdan ( ). Alt işlem sona erdiğinde, kilit otomatik olarak serbest bırakılır, çünkü tutma işlemi yoktur.
clacke

158

"Kilit dosyaları" nın varlığını test eden tüm yaklaşımlar kusurludur.

Neden? Çünkü bir dosyanın var olup olmadığını kontrol etmenin ve tek bir atomik eylemde yaratmanın bir yolu yoktur. Bu nedenle; karşılıklı dışlama molasında teşebbüsler YAPACAK bir yarış koşulu vardır .

Bunun yerine, kullanmanız gerekir mkdir. mkdirhenüz yoksa bir dizin oluşturur ve varsa bir çıkış kodu ayarlar. Daha da önemlisi, tüm bunları tek bir atomik eylemde yapar ve bu senaryo için mükemmel hale getirir.

if ! mkdir /tmp/myscript.lock 2>/dev/null; then
    echo "Myscript is already running." >&2
    exit 1
fi

Tüm ayrıntılar için mükemmel BashFAQ'ya bakın: http://mywiki.wooledge.org/BashFAQ/045

Eski kilitlere dikkat etmek istiyorsanız, ısıtıcı (1) işe yarar. Buradaki tek dezavantaj, operasyonun yaklaşık bir saniye sürmesi, bu yüzden anında değil.

Kaynaştırıcı kullanarak sorunu çözen bir kez yazdığım bir fonksiyon:

#       mutex file
#
# Open a mutual exclusion lock on the file, unless another process already owns one.
#
# If the file is already locked by another process, the operation fails.
# This function defines a lock on a file as having a file descriptor open to the file.
# This function uses FD 9 to open a lock on the file.  To release the lock, close FD 9:
# exec 9>&-
#
mutex() {
    local file=$1 pid pids 

    exec 9>>"$file"
    { pids=$(fuser -f "$file"); } 2>&- 9>&- 
    for pid in $pids; do
        [[ $pid = $$ ]] && continue

        exec 9>&- 
        return 1 # Locked by a pid.
    done 
}

Şöyle bir komut dosyasında kullanabilirsiniz:

mutex /var/run/myscript.lock || { echo "Already running." >&2; exit 1; }

Taşınabilirliği umursamıyorsanız (bu çözümler hemen hemen her UNIX kutusunda çalışmalıdır), Linux'un kaynaştırıcısı (1) bazı ek seçenekler sunar ve ayrıca sürü (1) vardır .


1
if ! mkdirParçayı, lockdir içinde saklanan PID (başarılı başlatma sırasında) ile gerçekten çalışıp çalışmadığını ve stalenes koruması için komut dosyasıyla aynı olup olmadığını kontrol ederek birleştirebilirsiniz . Bu aynı zamanda yeniden başlatmadan sonra PID'nin tekrar kullanılmasına karşı koruma sağlar ve hatta gerektirmez fuser.
Tobias Kienzler

4
O kesinlikle doğru mkdirdeğildir tanımlanan bir atom operasyon olmaya ve bu şekilde "yan etki" dosya sisteminin bir uygulama ayrıntı olduğunu. NFS'nin atomik bir şekilde uygulamadığını söylüyorsa ona tamamen inanıyorum. Yine /tmpde bir NFS payı olacağınızdan ve muhtemelen mkdiratomik olarak uygulanan bir fs tarafından sağlanacağından şüphelenmiyorum .
lhunath

5
Ancak normal bir dosyanın varlığını kontrol etmenin ve yapmazsa atomik lnolarak oluşturmanın bir yolu vardır: başka bir dosyadan sabit bir bağlantı oluşturmak için kullanma . Bunu garanti etmeyen garip dosya sistemleriniz varsa, orijinal dosyanın aynı olup olmadığını görmek için daha sonra yeni dosyanın inode'unu kontrol edebilirsiniz.
Juan Cespedes

4
Orada ise bu - 'Bir dosya var olup olmadığını kontrol etmek ve tek bir atom eylem oluşturmak için bir yol' open(... O_CREAT|O_EXCL). Bunun için lockfile-create(in lockfile-progs) veya dotlockfile(in liblockfile-bin) gibi uygun bir kullanıcı programına ihtiyacınız vardır . Ve düzgün temizlediğinizden (örn. trap EXIT) Veya eskiyen kilitleri (örneğin ile --use-pid) test ettiğinizden emin olun .
Toby Speight

5
"Kilit dosyaları" nın varlığını test eden tüm yaklaşımlar kusurludur. Neden? Çünkü bir dosyanın var olup olmadığını kontrol etmenin ve tek bir atomik eylemde yaratmanın bir yolu yoktur. "- Atomik hale getirmek için çekirdek seviyesi - ve çekirdek düzeyinde en az 2006 yılından bu yana var olan adam telif hakkı tarihinden görünen linux.die.net/man/1/flock (1) linux.die ile yapılır. 1), kişisel hiçbir şey, sadece çekirdek geliştiriciler tarafından sağlanan çekirdek uygulanan araçları kullanarak doğru olduğuna inanıyorum.
Craig Hicks

42

Flock (2) sistem çağrısının etrafında yaratıcı olmayan bir şekilde flock (1) adı verilen bir sarıcı var. Bu, temizleme vb. İçin endişelenmeden özel kilitleri güvenilir bir şekilde elde etmeyi nispeten kolaylaştırır. Man sayfasında bir kabuk betiğinde nasıl kullanılacağına dair örnekler vardır .


3
flock()Sistem çağrısı POSIX değildir ve NFS ayaklar üzerine dosyalar için çalışmaz.
maxschlepzig

17
flock -x -n %lock file% -c "%command%"Yalnızca bir örneğin yürütüldüğünden emin olmak için kullandığım bir Cron işinden kaçıyorum.
Ryall

Aww, yaratıcı olmayan sürü (1) yerine sürü (U) gibi bir şeyle gitmiş olmalıydı. ... biraz tanıdık var. . bir iki saniye önce duymuşum gibi görünüyor.
Kent Kruckeberg

Flock (2) dokümantasyonunun sadece dosyalarla kullanımını belirtmesi dikkat çekicidir, ancak flock (1) dokümantasyonunun dosya veya dizinle kullanımı belirtmesi önemlidir. Flock (1) belgeleri, oluşturma sırasında farkın nasıl belirtileceği konusunda açık değildir, ancak son bir "/" ekleyerek yapıldığını varsayıyorum. Her neyse, flok (1) dizinleri işleyebiliyorsa, ancak flok (2) yapamıyorsa, flok (1) sadece sürü (2) üzerine uygulanmaz.
Craig Hicks

27

Flok gibi atomik bir operasyona ihtiyacınız var, aksi takdirde bu başarısız olur.

Ancak sürü yoksa ne yapmalı. Mkdir var. Bu da atomik bir işlem. Sadece bir işlem başarılı bir mkdir ile sonuçlanır, diğer tüm işlemler başarısız olur.

Yani kod:

if mkdir /var/lock/.myscript.exclusivelock
then
  # do stuff
  :
  rmdir /var/lock/.myscript.exclusivelock
fi

Sen eskiyen kilitleri dikkat çekmek gerekir betiğiniz bir daha asla çalışmayacak bir çökme sonrası.


1
Bunu aynı anda birkaç kez çalıştırın ("./a.sh & ./a.sh & ./a.sh & ./a.sh & ./a.sh & ./a.sh & ./a.sh gibi) & ") ve komut dosyası birkaç kez sızacak.
Nippysaurus

7
@Nippysaurus: Bu kilitleme yöntemi sızmıyor. Gördüğünüz şey, tüm kopyalar başlatılmadan önce sona eren ilk komut dosyasıydı, böylece başka bir tane (doğru) kilidi alabildi. Bu yanlış pozitif önlemek için, sleep 10önce bir ekleyin rmdirve tekrar art arda deneyin - hiçbir şey "sızıntı".
Sir Athos

Diğer kaynaklar mkdir'in NFS gibi bazı dosya sistemlerinde atomik olmadığını iddia ediyor. Ve btw NFS'de eşzamanlı özyinelemeli mkdir'in bazen jenkins matris işlerinde hatalara yol açtığı durumlar gördüm. Bu yüzden eminim. Ancak mkdir, IMO'nun daha az talep edilen kullanım durumları için oldukça güzel.
akostadinov

Bash'es noclobber seçeneğini normal dosyalarla kullanabilirsiniz.
Palec

26

Kilitlemeyi güvenilir kılmak için atomik bir işlem yapmanız gerekir. Yukarıdaki tekliflerin çoğu atomik değildir. Önerilen lockfile (1) yardımcı programı, "NFS-dayanıklı" olduğundan bahsettiğimiz man sayfası olarak umut vericidir. İşletim sisteminiz kilit dosyasını (1) desteklemiyorsa ve çözümünüzün NFS üzerinde çalışması gerekiyorsa, çok fazla seçeneğiniz yoktur ....

NFSv2'nin iki atomik işlemi vardır:

  • Sembolik bağ
  • Adını değiştirmek

NFSv3 ile oluşturma çağrısı da atomiktir.

Dizin işlemleri NFSv2 ve NFSv3 altında atomik DEĞİLDİR (lütfen Brent Callaghan'ın 'NFS Illustrated' kitabına bakın, ISBN 0-201-32570-5; Brent Sun'ta NFS emektarıdır).

Bunu bilerek, dosyalar ve dizinler için spin kilitleri uygulayabilirsiniz (PHP'de değil kabukta):

akım yönünü kilitle:

while ! ln -s . lock; do :; done

bir dosyayı kilitle:

while ! ln -s ${f} ${f}.lock; do :; done

geçerli dir kilidini (varsayım, çalışan süreç gerçekten kilidi aldı):

mv lock deleteme && rm deleteme

bir dosyanın kilidini açın (varsayım, çalışan işlem gerçekten kilidi aldı):

mv ${f}.lock ${f}.deleteme && rm ${f}.deleteme

Kaldır da atomik değildir, bu nedenle önce yeniden adlandır (atomiktir) ve sonra kaldırır.

Symlink ve yeniden adlandırma çağrıları için, her iki dosya adının da aynı dosya sisteminde bulunması gerekir. Teklifim: sadece basit dosya adlarını kullanın (yol yok) ve aynı dizine dosya ve kilidi koyun.


NFS Illustrated'ın hangi sayfaları mkdir'in NFS üzerinden atomik olmadığı bildirisini destekler?
maxschlepzig

Bu teknik için teşekkürler. Yeni kabuk kitaplığımda bir kabuk muteks uygulaması var: github.com/Offirmo/offirmo-shell-lib , bkz. " Mutex ". lockfileVarsa kullanır veya symlinkyoksa bu yönteme geri döner .
Offirmo

Güzel. Ne yazık ki bu yöntem eski kilitleri otomatik olarak silmek için bir yol sağlamaz.
Richard Hansen

İki aşamalı kilit açma için ( mv, rm), gerektiği rm -fziyade, kullanılacak rmiki süreç P1, P2 yarışıyor durumda? Örneğin, P1 kilidi açma ile başlar mv, sonra P2 kilitlenir, sonra P2 kilidi açılır (hem mvve hem de rm), sonunda P1 dener rmve başarısız olur.
Matt Wallis

1
@MattWallis son sorun kolayca dahil ederek hafifletilebilir O $$içinde ${f}.deletemedosya.
Stefan Majewsky

23

Başka bir seçenek, shell'in noclobberseçeneğini koşarak kullanmaktır set -C. Sonra >dosya zaten varsa başarısız olur.

Kısaca:

set -C
lockfile="/tmp/locktest.lock"
if echo "$$" > "$lockfile"; then
    echo "Successfully acquired lock"
    # do work
    rm "$lockfile"    # XXX or via trap - see below
else
    echo "Cannot acquire lock - already locked by $(cat "$lockfile")"
fi

Bu, kabuğun çağrılmasına neden olur:

open(pathname, O_CREAT|O_EXCL)

Bu, atomik olarak dosyayı oluşturur veya dosya zaten varsa başarısız olur.


BashFAQ 045 hakkındaki bir yoruma göre , bu başarısız olabilir ksh88, ancak tüm kabuklarımda çalışır:

$ strace -e trace=creat,open -f /bin/bash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3

$ strace -e trace=creat,open -f /bin/zsh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_LARGEFILE, 0666) = 3

$ strace -e trace=creat,open -f /bin/pdksh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_LARGEFILE, 0666) = 3

$ strace -e trace=creat,open -f /bin/dash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3

Bayrağı pdkshekleyen ilginç O_TRUNC, ama açıkçası gereksiz:
boş bir dosya oluşturuyorsunuz veya hiçbir şey yapmıyorsunuz.


Nasıl yapacağınız, rmkirli çıkışların nasıl işlenmesini istediğinize bağlıdır.

Temiz çıkışta sil

Temiz olmayan çıkışa neden olan sorun çözülene ve kilit dosyası el ile kaldırılıncaya kadar yeni işlemler başarısız olur.

# acquire lock
# do work (code here may call exit, etc.)
rm "$lockfile"

Herhangi bir çıkışta sil

Komut dosyası zaten çalışmıyorsa yeni çalışmalar başarılı olur.

trap 'rm "$lockfile"' EXIT

Çok yeni bir yaklaşım ... Bu, bir kilit dizini yerine bir kilit dosyası kullanarak atomikliği başarmanın bir yolu gibi görünüyor.
Matt Caldwell

Güzel yaklaşım. :-) EXIT tuzağında hangi işlemin kilit dosyasını temizleyebileceğini kısıtlamalıdır. Örneğin: '[[$ (cat "$ lockfile") == "$$"]]; sonra rm "$ lockfile"; fi 'EXIT
Kevin Seifert

1
Kilit dosyaları NFS üzerinden atomik değildir. bu yüzden insanlar kilit dizinlerini kullanmaya başladılar.
K Richard Pixley

20

Bunun GNU Paralleliçin olarak adlandırıldığında bir muteks olarak çalıştığı için kullanabilirsiniz sem. Yani, somut terimlerle, şunları kullanabilirsiniz:

sem --id SCRIPTSINGLETON yourScript

Siz de bir zaman aşımı istiyorsanız, şunu kullanın:

sem --id SCRIPTSINGLETON --semaphoretimeout -10 yourScript

<0 zaman aşımı, semafor zaman aşımı içinde serbest bırakılmazsa komut dosyası çalıştırmadan çıkış anlamına gelir,> 0 zaman aşımı komut dosyasını yine de çalıştırmak anlamına gelir.

--idOna, kontrol terminaline varsayılan olarak başka bir ad vermeniz gerektiğini unutmayın .

GNU Parallel Linux / OSX / Unix platformlarının çoğunda çok basit bir kurulumdur - sadece bir Perl betiğidir.


Çok kötü insanlar yararsız cevapları küçümsemek konusunda isteksizdir: bu, yeni ilgili cevapların bir yığın yığınına gömülmesine yol açar.
Dmitry Grigoryev

4
Sadece çok sayıda oy almamız lazım. Bu çok düzenli ve az bilinen bir cevap. (Pedantik olmasına rağmen OP hızlı ve kirli olmasına rağmen bu hızlı ve temiz!) semİlgili soru hakkında daha fazla bilgi unix.stackexchange.com/a/322200/199525 .
Parçalı Bulutlu

16

Kabuk betikleri için, kilitleri daha taşınabilir hale getirdiği için mkdirüstünden geçme eğilimindeyim flock.

Her iki durumda da kullanmak set -eyeterli değildir. Bu komut dosyasından yalnızca bir komut başarısız olursa çıkar. Kilitleriniz hala geride kalacak.

Düzgün kilit temizliği için, tuzaklarınızı gerçekten bu psuedo kodu (kaldırılmış, basitleştirilmiş ve test edilmemiş ancak aktif olarak kullanılan komut dosyalarından) gibi bir şeye ayarlamanız gerekir:

#=======================================================================
# Predefined Global Variables
#=======================================================================

TMPDIR=/tmp/myapp
[[ ! -d $TMP_DIR ]] \
    && mkdir -p $TMP_DIR \
    && chmod 700 $TMPDIR

LOCK_DIR=$TMP_DIR/lock

#=======================================================================
# Functions
#=======================================================================

function mklock {
    __lockdir="$LOCK_DIR/$(date +%s.%N).$$" # Private Global. Use Epoch.Nano.PID

    # If it can create $LOCK_DIR then no other instance is running
    if $(mkdir $LOCK_DIR)
    then
        mkdir $__lockdir  # create this instance's specific lock in queue
        LOCK_EXISTS=true  # Global
    else
        echo "FATAL: Lock already exists. Another copy is running or manually lock clean up required."
        exit 1001  # Or work out some sleep_while_execution_lock elsewhere
    fi
}

function rmlock {
    [[ ! -d $__lockdir ]] \
        && echo "WARNING: Lock is missing. $__lockdir does not exist" \
        || rmdir $__lockdir
}

#-----------------------------------------------------------------------
# Private Signal Traps Functions {{{2
#
# DANGER: SIGKILL cannot be trapped. So, try not to `kill -9 PID` or 
#         there will be *NO CLEAN UP*. You'll have to manually remove 
#         any locks in place.
#-----------------------------------------------------------------------
function __sig_exit {

    # Place your clean up logic here 

    # Remove the LOCK
    [[ -n $LOCK_EXISTS ]] && rmlock
}

function __sig_int {
    echo "WARNING: SIGINT caught"    
    exit 1002
}

function __sig_quit {
    echo "SIGQUIT caught"
    exit 1003
}

function __sig_term {
    echo "WARNING: SIGTERM caught"    
    exit 1015
}

#=======================================================================
# Main
#=======================================================================

# Set TRAPs
trap __sig_exit EXIT    # SIGEXIT
trap __sig_int INT      # SIGINT
trap __sig_quit QUIT    # SIGQUIT
trap __sig_term TERM    # SIGTERM

mklock

# CODE

exit # No need for cleanup code here being in the __sig_exit trap function

İşte olacak. Tüm tuzaklar bir çıkış üretecek, böylece fonksiyon __sig_exither zaman gerçekleşecek (bir SIGKILL'i engelliyor), bu da kilitlerinizi temizler.

Not: Çıkış değerlerim düşük değerler değil. Neden? Çeşitli toplu işlem sistemleri 0 ile 31 arasındaki sayıları yapar veya beklentileri vardır. Bunları başka bir şeye ayarlayarak, komut dosyalarımın ve toplu akışlarımın önceki toplu iş veya komut dosyasına uygun şekilde tepki vermesini sağlayabilirim.


2
Senaryonuz çok ayrıntılı, sanırım çok daha kısa olabilirdi, ama genel olarak, evet, bunu doğru yapmak için tuzaklar kurmalısınız. Ayrıca SIGHUP ekleyeceğim.
mojuba

$ LOCK_DIR için kontrol edilir gibi görünüyor, oysa $ __ lockdir kaldırır. Belki kilidi kaldırırken rm -r $ LOCK_DIR yapmasını öneririm?
bevada

Öneri için teşekkürler. Yukarıdaki kod kaldırıldı ve bir psuedo kodu tarzında yerleştirildi, bu yüzden millet kullanımına göre ayarlanması gerekecektir. Rmdir güvenle dizinleri kaldırır Ancak, bile bile benim durumumda rmdir gitti only_if onlar boştur. Eğer insanlar PID dosyaları vb. Gibi kaynakları yerleştiriyorlarsa, kilit temizlemelerini daha agresif olarak değiştirmeli rm -r $LOCK_DIRveya gerektiğinde zorlamalıdırlar (göreceli çizik dosyalarını tutma gibi özel durumlarda da yaptığım gibi). Şerefe.
Mark Stinson

Test yaptın mı exit 1002?
Gilles Quenot

13

Gerçekten hızlı ve gerçekten kirli mi? Betiğinizin üst kısmındaki bu tek satırlı çalışma:

[[ $(pgrep -c "`basename \"$0\"`") -gt 1 ]] && exit

Elbette, komut dosyası adınızın benzersiz olduğundan emin olun. :)


Test etmek için bunu nasıl simüle edebilirim? Bir komut dosyasını bir satırda iki kez başlatmanın ve zaten çalışıyorsa belki bir uyarı almanın bir yolu var mı?
rubo77

2
Bu hiç çalışmıyor! Neden kontrol etmeliyim -gt 2? grep her zaman ps sonucunda kendini bulamaz!
rubo77

pgrepPOSIX'te değil. Bunu taşınabilir olarak çalıştırmak istiyorsanız, POSIX'e ihtiyacınız var psve çıktısını işliyorsunuz .
Palec

OSX'te -cmevcut değil, kullanmanız gerekecek | wc -l. Sayı karşılaştırması hakkında: -gt 1ilk örnek kendini gördüğünden beri kontrol edilir.
Benjamin Peter

6

İşte atom dizini kilitlemesini PID üzerinden eski kilit kontrolü ile birleştiren ve eskiyse yeniden başlatan bir yaklaşım. Ayrıca, bu herhangi bir bashisms'e dayanmaz.

#!/bin/dash

SCRIPTNAME=$(basename $0)
LOCKDIR="/var/lock/${SCRIPTNAME}"
PIDFILE="${LOCKDIR}/pid"

if ! mkdir $LOCKDIR 2>/dev/null
then
    # lock failed, but check for stale one by checking if the PID is really existing
    PID=$(cat $PIDFILE)
    if ! kill -0 $PID 2>/dev/null
    then
       echo "Removing stale lock of nonexistent PID ${PID}" >&2
       rm -rf $LOCKDIR
       echo "Restarting myself (${SCRIPTNAME})" >&2
       exec "$0" "$@"
    fi
    echo "$SCRIPTNAME is already running, bailing out" >&2
    exit 1
else
    # lock successfully acquired, save PID
    echo $$ > $PIDFILE
fi

trap "rm -rf ${LOCKDIR}" QUIT INT TERM EXIT


echo hello

sleep 30s

echo bye

5

Bilinen bir konumda bir kilit dosyası oluşturun ve kod başlangıcında varlığını kontrol ettiniz mi? Birisi komut dosyasının yürütülmesini engelleyen hatalı bir örneği izlemeye çalışıyorsa PID'yi dosyaya koymak yardımcı olabilir.


5

Bu örnek adam sürüsünde açıklanmıştır, ancak bazı iyileştirmelere ihtiyaç vardır, çünkü hataları ve çıkış kodlarını yönetmeliyiz:

   #!/bin/bash
   #set -e this is useful only for very stupid scripts because script fails when anything command exits with status more than 0 !! without possibility for capture exit codes. not all commands exits >0 are failed.

( #start subprocess
  # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
  flock -x -w 10 200
  if [ "$?" != "0" ]; then echo Cannot lock!; exit 1; fi
  echo $$>>/var/lock/.myscript.exclusivelock #for backward lockdir compatibility, notice this command is executed AFTER command bottom  ) 200>/var/lock/.myscript.exclusivelock.
  # Do stuff
  # you can properly manage exit codes with multiple command and process algorithm.
  # I suggest throw this all to external procedure than can properly handle exit X commands

) 200>/var/lock/.myscript.exclusivelock   #exit subprocess

FLOCKEXIT=$?  #save exitcode status
    #do some finish commands

exit $FLOCKEXIT   #return properly exitcode, may be usefull inside external scripts

Başka bir yöntem kullanabilir, geçmişte kullandığım işlemleri listeleyebilirsiniz. Ancak bu, yukarıdaki yöntemden daha karmaşıktır. Ps ile süreçleri listelemek, adına göre filtre, parazit kaldırmak için ek filtre grep -v grep nihayet grep -c ile saymak. ve sayı ile karşılaştırın. Karmaşık ve belirsiz


1
Ln -s komutunu kullanabilirsiniz, çünkü bu yalnızca mkdir ile aynı dosya veya sembol bağlantısı olmadığında sembolik bağlantı oluşturabilir. geçmişte semboller kullanılan birçok sistem işlemi, örneğin init veya inetd. synlink işlem kimliğini korur, ancak gerçekten hiçbir şeye işaret etmez. yıllar boyunca bu davranış değişti. süreçleri sürüler ve semaforlar kullanır.
Znik

5

Gönderilen mevcut yanıtlar ya CLI yardımcı programına dayanır flockya da kilit dosyasını düzgün şekilde korumaz. Flock yardımcı programı tüm Linux dışı sistemlerde (yani FreeBSD) kullanılamaz ve NFS'de düzgün çalışmaz.

Sistem yönetiminin ve sistem geliştirmenin ilk günlerinde, kilit dosyası oluşturmak için güvenli ve nispeten taşınabilir bir yöntemin, mkemp(3)veya kullanarak geçici bir dosya oluşturmak olduğu söylendi.mkemp(1) , geçici dosya (yani PID) için tanımlayıcı bilgileri yazma, daha sonra sabit bağlantı geçici dosyayı kilit dosyasına. Bağlantı başarılı olursa, kilidi başarıyla elde ettiniz.

Kabuk komut dosyalarında kilitleri kullanırken, genellikle obtain_lock()paylaşılan bir profile bir işlev yerleştirir ve komut dosyalarından kaynaklanırım. Aşağıda kilit işlevime bir örnek verilmiştir:

obtain_lock()
{
  LOCK="${1}"
  LOCKDIR="$(dirname "${LOCK}")"
  LOCKFILE="$(basename "${LOCK}")"

  # create temp lock file
  TMPLOCK=$(mktemp -p "${LOCKDIR}" "${LOCKFILE}XXXXXX" 2> /dev/null)
  if test "x${TMPLOCK}" == "x";then
     echo "unable to create temporary file with mktemp" 1>&2
     return 1
  fi
  echo "$$" > "${TMPLOCK}"

  # attempt to obtain lock file
  ln "${TMPLOCK}" "${LOCK}" 2> /dev/null
  if test $? -ne 0;then
     rm -f "${TMPLOCK}"
     echo "unable to obtain lockfile" 1>&2
     if test -f "${LOCK}";then
        echo "current lock information held by: $(cat "${LOCK}")" 1>&2
     fi
     return 2
  fi
  rm -f "${TMPLOCK}"

  return 0;
};

Kilit işlevinin nasıl kullanılacağına ilişkin bir örnek aşağıdadır:

#!/bin/sh

. /path/to/locking/profile.sh
PROG_LOCKFILE="/tmp/myprog.lock"

clean_up()
{
  rm -f "${PROG_LOCKFILE}"
}

obtain_lock "${PROG_LOCKFILE}"
if test $? -ne 0;then
   exit 1
fi
trap clean_up SIGHUP SIGINT SIGTERM

# bulk of script

clean_up
exit 0
# end of script

Sesli aramayı unutmayın clean_upSenaryonuzdaki herhangi bir çıkış noktasını .

Yukarıdakileri hem Linux hem de FreeBSD ortamlarında kullandım.


4

Bir Debian makinesini hedeflerken lockfile-progspaketi iyi bir çözüm olarak görüyorum . procmailAyrıca bir lockfilearaç ile geliyor . Ancak bazen bunlardan hiçbirine takılı kalmam.

İşte mkdireski kilitleri tespit etmek için atomiklik ve bir PID dosyası için kullanılan çözümüm . Bu kod şu anda bir Cygwin kurulumunda üretilmektedir ve iyi çalışır.

Kullanmak exclusive_lock_requireiçin bir şeye özel erişim almanız gerektiğinde arayın . İsteğe bağlı bir kilit adı parametresi, kilitleri farklı komut dosyaları arasında paylaşmanıza olanak tanır. Daha karmaşık bir şeye ihtiyacınız varsa, iki alt seviye işlevi de vardır ( exclusive_lock_tryve exclusive_lock_retry).

function exclusive_lock_try() # [lockname]
{

    local LOCK_NAME="${1:-`basename $0`}"

    LOCK_DIR="/tmp/.${LOCK_NAME}.lock"
    local LOCK_PID_FILE="${LOCK_DIR}/${LOCK_NAME}.pid"

    if [ -e "$LOCK_DIR" ]
    then
        local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
        if [ ! -z "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2> /dev/null
        then
            # locked by non-dead process
            echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
            return 1
        else
            # orphaned lock, take it over
            ( echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null && local LOCK_PID="$$"
        fi
    fi
    if [ "`trap -p EXIT`" != "" ]
    then
        # already have an EXIT trap
        echo "Cannot get lock, already have an EXIT trap"
        return 1
    fi
    if [ "$LOCK_PID" != "$$" ] &&
        ! ( umask 077 && mkdir "$LOCK_DIR" && umask 177 && echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null
    then
        local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
        # unable to acquire lock, new process got in first
        echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
        return 1
    fi
    trap "/bin/rm -rf \"$LOCK_DIR\"; exit;" EXIT

    return 0 # got lock

}

function exclusive_lock_retry() # [lockname] [retries] [delay]
{

    local LOCK_NAME="$1"
    local MAX_TRIES="${2:-5}"
    local DELAY="${3:-2}"

    local TRIES=0
    local LOCK_RETVAL

    while [ "$TRIES" -lt "$MAX_TRIES" ]
    do

        if [ "$TRIES" -gt 0 ]
        then
            sleep "$DELAY"
        fi
        local TRIES=$(( $TRIES + 1 ))

        if [ "$TRIES" -lt "$MAX_TRIES" ]
        then
            exclusive_lock_try "$LOCK_NAME" > /dev/null
        else
            exclusive_lock_try "$LOCK_NAME"
        fi
        LOCK_RETVAL="${PIPESTATUS[0]}"

        if [ "$LOCK_RETVAL" -eq 0 ]
        then
            return 0
        fi

    done

    return "$LOCK_RETVAL"

}

function exclusive_lock_require() # [lockname] [retries] [delay]
{
    if ! exclusive_lock_retry "$@"
    then
        exit 1
    fi
}

Teşekkürler, cygwin üzerinde kendim denedim ve basit testlerden geçti.
ndemou

4

Eğer bu iş parçacığının başka bir yerinde tarif edilmiş olan flok sınırlamaları sizin için bir sorun değilse, bu işe yarayacaktır:

#!/bin/bash

{
    # exit if we are unable to obtain a lock; this would happen if 
    # the script is already running elsewhere
    # note: -x (exclusive) is the default
    flock -n 100 || exit

    # put commands to run here
    sleep 100
} 100>/tmp/myjob.lock 

3
Sadece -x (yazma kilidi) varsayılan olarak ayarlanmış olduğunu işaret edeceğini düşündüm.
Keldon Alleyne

ve kilidi alamazsa hemen -ngeçecekexit 1
Anentropik

Teşekkürler @KeldonAlleyne, varsayılan olduğu için "-x" kaldırmak için kodu güncelledim.
presto8

3

Bazı unixler, lockfiledaha önce bahsedilenlere çok benzer flock.

Manpage'den:

lockfile bir veya daha fazla semafor dosyası oluşturmak için kullanılabilir. Kilit dosyası belirtilen tüm dosyaları (belirtilen sırada) oluşturamazsa, uyku süresi (varsayılan değer 8'dir) saniye bekler ve başarısız olan son dosyayı yeniden dener. Hata döndürülünceye kadar yapılacak yeniden deneme sayısını belirleyebilirsiniz. Yeniden deneme sayısı -1 ise (varsayılan, yani -r-1) lockfile sonsuza kadar yeniden dener.


nasıl lockfileyardımcı oluruz ??
Offirmo

lockfileile dağıtılır procmail. Ayrıca paketle dotlockfilebirlikte gelen bir alternatif var liblockfile. İkisi de NFS üzerinde güvenilir bir şekilde çalıştıklarını iddia ediyorlar.
Bay Ölümsüz

3

Aslında bmdhacks'ın cevabı neredeyse iyi olsa da, ikinci betiğin önce kilit dosyasını kontrol ettikten ve yazmadan önce çalıştırma şansı azdır. Yani her ikisi de kilit dosyasını yazacak ve her ikisi de çalışıyor olacak. Bunu nasıl çalıştıracağınız aşağıda açıklanmıştır:

lockfile=/var/lock/myscript.lock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null ; then
  trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
else
  # or you can decide to skip the "else" part if you want
  echo "Another instance is already running!"
fi

noclobberSeçenek emin dosyası zaten varsa yönlendirme komutunun başarısız olacağını yapacaktır. Yönlendirme komutu aslında atomiktir - dosyayı bir komutla yazar ve kontrol edersiniz. Dosyanın sonundaki kilit dosyasını kaldırmanıza gerek yoktur - tuzak tarafından kaldırılır. Umarım bu daha sonra okuyacak insanlara yardımcı olur.

PS Mikel'in soruyu doğru cevapladığını görmedim, ancak komut dosyasının Ctrl-C ile durdurulduktan sonra kilit dosyasının bırakılma olasılığını azaltmak için tuzak komutunu içermedi. İşte bu tam çözüm


3

Lockfiles, lockdirs, özel kilitleme programları ve pidoftüm Linux kurulumlarında bulunmadığı için ortadan kaldırmak istedim . Ayrıca mümkün olan en basit koda (veya en azından mümkün olduğunca az satıra) sahip olmak istedim. ifTek satırda en basit ifade:

if [[ $(ps axf | awk -v pid=$$ '$1!=pid && $6~/'$(basename $0)'/{print $1}') ]]; then echo "Already running"; exit; fi

1
Bu, makinemdeki 'ps' çıkışına duyarlıdır (Ubuntu 14.04, procps-ng sürüm 3.3.9'daki / bin / ps) 'ps axf' komutu, alan numaralarını bozan ascii ağaç karakterlerini yazdırır. Bu benim için çalıştı: /bin/ps -a --format pid,cmd | awk -v pid=$$ '/'$(basename $0)'/ { if ($1!=pid) print $1; }'
qneill

2

Eski kilit dosyalarını işleyen basit bir yaklaşım kullanıyorum.

Pid'i saklayan yukarıdaki çözümlerin bazılarının, pid'in etrafına sarılabileceğini göz ardı ettiğini unutmayın. Bu nedenle, saklanan pid ile geçerli bir işlem olup olmadığını kontrol etmek, özellikle uzun süre çalışan komut dosyaları için yeterli değildir.

Bir kerede yalnızca bir komut dosyasının açılıp yazabileceğinden emin olmak için noclobber kullanıyorum. Ayrıca, kilit dosyasındaki bir işlemi benzersiz olarak tanımlamak için yeterli bilgi saklıyorum. Ben pid, ppid, lstart olmak üzere bir süreci benzersiz olarak tanımlamak için veri kümesini tanımlarım.

Yeni bir komut dosyası başlatıldığında, kilit dosyasını oluşturamazsa, kilit dosyasını oluşturan işlemin hala etrafta olduğunu doğrular. Değilse, orijinal sürecin nankör bir ölümle öldüğünü ve eski bir kilit dosyası bıraktığını varsayıyoruz. Yeni komut dosyası daha sonra kilit dosyasının sahipliğini alır ve her şey yolundadır.

Birden fazla platformda birden fazla kabukla çalışmalıdır. Hızlı, taşınabilir ve basit.

#!/usr/bin/env sh
# Author: rouble

LOCKFILE=/var/tmp/lockfile #customize this line

trap release INT TERM EXIT

# Creates a lockfile. Sets global variable $ACQUIRED to true on success.
# 
# Returns 0 if it is successfully able to create lockfile.
acquire () {
    set -C #Shell noclobber option. If file exists, > will fail.
    UUID=`ps -eo pid,ppid,lstart $$ | tail -1`
    if (echo "$UUID" > "$LOCKFILE") 2>/dev/null; then
        ACQUIRED="TRUE"
        return 0
    else
        if [ -e $LOCKFILE ]; then 
            # We may be dealing with a stale lock file.
            # Bring out the magnifying glass. 
            CURRENT_UUID_FROM_LOCKFILE=`cat $LOCKFILE`
            CURRENT_PID_FROM_LOCKFILE=`cat $LOCKFILE | cut -f 1 -d " "`
            CURRENT_UUID_FROM_PS=`ps -eo pid,ppid,lstart $CURRENT_PID_FROM_LOCKFILE | tail -1`
            if [ "$CURRENT_UUID_FROM_LOCKFILE" == "$CURRENT_UUID_FROM_PS" ]; then 
                echo "Script already running with following identification: $CURRENT_UUID_FROM_LOCKFILE" >&2
                return 1
            else
                # The process that created this lock file died an ungraceful death. 
                # Take ownership of the lock file.
                echo "The process $CURRENT_UUID_FROM_LOCKFILE is no longer around. Taking ownership of $LOCKFILE"
                release "FORCE"
                if (echo "$UUID" > "$LOCKFILE") 2>/dev/null; then
                    ACQUIRED="TRUE"
                    return 0
                else
                    echo "Cannot write to $LOCKFILE. Error." >&2
                    return 1
                fi
            fi
        else
            echo "Do you have write permissons to $LOCKFILE ?" >&2
            return 1
        fi
    fi
}

# Removes the lock file only if this script created it ($ACQUIRED is set), 
# OR, if we are removing a stale lock file (first parameter is "FORCE") 
release () {
    #Destroy lock file. Take no prisoners.
    if [ "$ACQUIRED" ] || [ "$1" == "FORCE" ]; then
        rm -f $LOCKFILE
    fi
}

# Test code
# int main( int argc, const char* argv[] )
echo "Acquring lock."
acquire
if [ $? -eq 0 ]; then 
    echo "Acquired lock."
    read -p "Press [Enter] key to release lock..."
    release
    echo "Released lock."
else
    echo "Unable to acquire lock."
fi

Size farklı bir çözüm için +1 verdim. AIX'te çalışmaz (> ps -eo pid, ppid, lstart $$ | tail -1 ps: -o ile geçersiz liste). HP-UX (> ps -eo pid, ppid, lstart $$ kuyruk -1 ps: geçersiz seçenek - o). Teşekkürler.
Tagar

2

Bu satırı komut dosyanızın başına ekleyin

[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :

Bu adam sürüsünden kaynak kodudur.

Daha fazla günlük kaydı istiyorsanız, bunu kullanın

[ "${FLOCKER}" != "$0" ] && { echo "Trying to start build from queue... "; exec bash -c "FLOCKER='$0' flock -E $E_LOCKED -en '$0' '$0' '$@' || if [ \"\$?\" -eq $E_LOCKED ]; then echo 'Locked.'; fi"; } || echo "Lock is free. Completing."

Bu, flockyardımcı programı kullanarak kilitleri ayarlar ve denetler . Bu kod, FLOCKER değişkenini kontrol ederek ilk kez çalıştırılıp çalıştırılmadığını algılar, komut dosyası adına ayarlanmadıysa, flock kullanarak ve FLOCKER değişkeni başlatılırken komut dosyasını tekrar tekrar başlatmaya çalışır, ardından FLOCKER doğru ayarlanmışsa, önceki yinelemede akın başarılı oldu ve devam etmek sorun değil. Kilit meşgulse, yapılandırılabilir çıkış koduyla başarısız olur.

Debian 7 üzerinde çalışmıyor gibi görünüyor, ancak deneysel util-linux 2.25 paketiyle tekrar çalışıyor gibi görünüyor. "Flock: ... Metin dosyası meşgul" yazar. Komut dosyanızdaki yazma iznini devre dışı bırakarak geçersiz kılınabilir.


1

PID ve kilit dosyaları kesinlikle en güvenilir olanlardır. Programı çalıştırmayı denediğinizde, hangi kilit dosyasının bulunup bulunmadığını kontrol edebilir ve varsa pssürecin hala çalışıp çalışmadığını görmek için kullanabilir . Değilse, komut dosyası başlatılabilir ve kilit dosyasındaki PID'yi kendine günceller.


1

En azından kullanım durumum için bmdhack'ın çözümünün en pratik olduğunu düşünüyorum. Flock ve lockfile kullanmak, komut dosyası sona erdiğinde her zaman garanti edilemeyen rm kullanarak lockfile'ın kaldırılmasına dayanır (ör. -9 öldür).

Ben bmdhack'ın çözümü ile ilgili küçük bir şeyi değiştirirdim: Bu, bu semaforun güvenli çalışması için gereksiz olduğunu belirtmeden kilit dosyasını kaldırmanın bir noktasını oluşturuyor. Kill -0'ı kullanması, ölü bir işlem için eski bir kilit dosyasının yok sayılmasını / üzerine yazılmasını sağlar.

Bu nedenle basitleştirilmiş çözümüm, singletonunuzun üstüne aşağıdakileri eklemektir:

## Test the lock
LOCKFILE=/tmp/singleton.lock 
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
    echo "Script already running. bye!"
    exit 
fi

## Set the lock 
echo $$ > ${LOCKFILE}

Tabii ki, bu komut dosyası hala aynı anda başlaması muhtemel süreçlerin bir yarış tehlikesine sahip olduğu kusuruna sahiptir, çünkü kilit testi ve ayar işlemleri tek bir atomik eylem değildir. Ancak lhunath tarafından mkdir kullanmak için önerilen çözüm, öldürülen bir komut dosyasının dizinin arkasında bırakabileceği ve böylece diğer örneklerin çalışmasını engelleyebileceği kusuruna sahiptir.


1

Semaphoric yardımcı kullanımları flock(şekilde presto8 göre, örneğin yukarıda ele alınan) bir uygulamaya sayma semaforu . İstediğiniz belirli sayıda eşzamanlı işlemi etkinleştirir. Bunu, çeşitli kuyruk işçi işlemlerinin eşzamanlılık düzeyini sınırlamak için kullanırız.

Sem gibi ama çok daha hafif. (Tam açıklama: Sem'in ihtiyaçlarımız için çok ağır olduğunu ve basit bir sayım semaforu bulunmadığını bulduktan sonra yazdım.)


1

Flok (1) içeren ancak alt kabuğu olmayan bir örnek. flock () ed dosyası / tmp / foo hiçbir zaman kaldırılmaz, ancak flock () ve un-flock () ed sürümlerinin alınması önemli değildir.

#!/bin/bash

exec 9<> /tmp/foo
flock -n 9
RET=$?
if [[ $RET -ne 0 ]] ; then
    echo "lock failed, exiting"
    exit
fi

#Now we are inside the "critical section"
echo "inside lock"
sleep 5
exec 9>&- #close fd 9, and release lock

#The part below is outside the critical section (the lock)
echo "lock released"
sleep 5

1

Dış bağımlılıklara ihtiyaç duymadan milyonlarca kez cevap verdim, ancak başka bir yol:

LOCK_FILE="/var/lock/$(basename "$0").pid"
trap "rm -f ${LOCK_FILE}; exit" INT TERM EXIT
if [[ -f $LOCK_FILE && -d /proc/`cat $LOCK_FILE` ]]; then
   // Process already exists
   exit 1
fi
echo $$ > $LOCK_FILE

Geçerli PID'yi ($$) kilit dosyasına her yazdığında ve komut dosyası başlatıldığında bir işlemin en son PID ile çalışıp çalışmadığını kontrol eder.


1
Bindirme çağrısı (veya en azından normal durumun sonuna yakın bir temizleme) olmadan, kilit dosyasının son çalıştırmadan sonra bırakıldığı ve PID'nin başka bir işlem tarafından daha sonra yeniden kullanıldığı yanlış pozitif hataya sahip olursunuz. (Ve en kötü durumda, apache gibi uzun süren bir sürece aktarıldı ....)
Philippe Chaintreuil

1
Katılıyorum, yaklaşımım kusurlu, bir tuzağa ihtiyacı var. Çözümümü güncelledim. Hala dış bağımlılıkları olmamayı tercih ediyorum.
Filidor Wiese

1

Sürecin kilidini kullanmak çok daha güçlüdür ve nankör çıkışları da halleder. süreç çalıştığı sürece lock_file açık tutulur. Süreç gerçekleştikten sonra (kabukla) kapatılacaktır (öldürülse bile). Bunu çok verimli buldum:

lock_file=/tmp/`basename $0`.lock

if fuser $lock_file > /dev/null 2>&1; then
    echo "WARNING: Other instance of $(basename $0) running."
    exit 1
fi
exec 3> $lock_file 

1

Ben oneliner @ betiğin en başında kullanıyorum:

#!/bin/bash

if [[ $(pgrep -afc "$(basename "$0")") -gt "1" ]]; then echo "Another instance of "$0" has already been started!" && exit; fi
.
the_beginning_of_actual_script

İşlemin bellekte varlığını görmek iyidir (işlemin durumu ne olursa olsun); ama bu işi benim için yapıyor.


0

Sürü yolu gitmek için bir yoldur. Senaryo aniden öldüğünde ne olacağını düşünün. Sürü durumunda sadece sürüyü kaybedersiniz, ancak bu bir sorun değildir. Ayrıca, kötü bir hile komut dosyasının kendisi bir sürü almak olduğunu unutmayın .. ama elbette tam buhar ileriye izin sorunları içine çalıştırmak sağlar.


0

Hızlı ve kirli?

#!/bin/sh

if [ -f sometempfile ]
  echo "Already running... will now terminate."
  exit
else
  touch sometempfile
fi

..do what you want here..

rm sometempfile

7
Bu, nasıl kullanıldığına bağlı olarak bir sorun olabilir veya olmayabilir, ancak kilidi test etmek ve oluşturmak arasında bir yarış durumu vardır, böylece iki komut dosyasının her ikisi de aynı anda başlatılabilir. Biri ilk olarak sonlanırsa, diğeri kilit dosyası olmadan çalışmaya devam eder.
TimB

3
C Taşınabilir kabuk komut dosyası hakkında bana çok şey öğretti, bir kilit yapmak için kullanılır. $$ dosyası ve daha sonra "kilit" ile bağlantı kurmaya çalışın - bağlantı başarılı olursa, kilit vardı, aksi takdirde kilidi kaldırdınız. ve çıkıldı.
Paul Tomblin

Bunu yapmak gerçekten iyi bir yoldur, ancak bir şeyler ters giderse ve lockfile silinmezse hala lockfile'ı kaldırmanız gerekir.
Matthew Scharley

2
Çabuk ve kirli, istediği şey :)
Aupajo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.