शेल स्क्रिप्ट का केवल एक उदाहरण सुनिश्चित करने का त्वरित और गंदा तरीका एक समय में चल रहा है


179

यह सुनिश्चित करने का एक त्वरित और गंदा तरीका है कि शेल स्क्रिप्ट का केवल एक उदाहरण एक निश्चित समय पर चल रहा है?


संबंधित @ Unix.SE: unix.stackexchange.com/questions/22044/…
Palec

जवाबों:


109

यहां एक कार्यान्वयन है जो लॉकफ़ाइल का उपयोग करता है और इसमें एक पीआईडी डालता है। यह एक सुरक्षा के रूप में कार्य करता है अगर प्रक्रिया को हटाने से पहले मार दिया जाता है :

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}

यहाँ ट्रिक वह है kill -0जो किसी भी सिग्नल को डिलीवर नहीं करता है, लेकिन यदि पीआईडी ​​के साथ कोई प्रक्रिया मौजूद है तो बस चेक करता है। इसके अलावा trapयह सुनिश्चित करने के लिए कॉल किया जा सकता है कि लॉकफ़ाइल को हटा दिया जाए, भले ही आपकी प्रक्रिया को मार दिया जाए (छोड़कर kill -9)।


73
जैसा कि पहले से ही एथेर उत्तर पर एक टिप्पणी में उल्लेख किया गया है, इसमें एक घातक दोष है - अगर चेक और इको के बीच दूसरी स्क्रिप्ट शुरू होती है, तो आप टोस्ट होते हैं।
पॉल टॉम्बलिन

1
सिमिलिंक ट्रिक साफ-सुथरी है, लेकिन अगर लॉकफाइल का मालिक किल -9’एल है या सिस्टम क्रैश हो जाता है, तो सिमिलिंक पढ़ने के लिए अभी भी एक रेस कंडीशन है, मालिक को नोटिस गया है, और फिर इसे हटा दें। मैं अपने समाधान के साथ चिपका रहा हूं।
bmdhacks

9
शेल (1) या लॉकफाइल (1) का उपयोग करके शेल में परमाणु जांच और निर्माण उपलब्ध है। अन्य उत्तर देखें।
dmckee --- पूर्व-मध्यस्थ बिल्ली का बच्चा

3
एक परमाणु जांच करने के पोर्टेबल तरीके के लिए मेरा जवाब देखें और झुंड या लॉकफाइल जैसी उपयोगिताओं पर भरोसा किए बिना बनाएं।
लुननाथ

2
यह परमाणु नहीं है और इस प्रकार बेकार है। आपको परीक्षण और सेट के लिए एक परमाणु तंत्र की आवश्यकता है।
के रिचर्ड पिक्ले

214

flock(1)फ़ाइल डिस्क्रिप्टर पर एक अनन्य स्कॉप्ड लॉक बनाने के लिए उपयोग करें । इस तरह आप स्क्रिप्ट के विभिन्न हिस्सों को सिंक्रोनाइज़ भी कर सकते हैं।

#!/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

यह उस कोड को बीच में सुनिश्चित करता है (और )एक समय में केवल एक प्रक्रिया द्वारा चलाया जाता है और यह प्रक्रिया एक लॉक के लिए बहुत लंबा इंतजार नहीं करती है।

कैविएट: यह विशेष कमांड का एक हिस्सा है util-linux। यदि आप लिनक्स के अलावा कोई ऑपरेटिंग सिस्टम चलाते हैं, तो यह उपलब्ध हो सकता है या नहीं भी हो सकता है।


11
200 क्या है? यह मैनुल में "एफडी" कहता है, लेकिन मुझे नहीं पता कि इसका क्या मतलब है।
चॉबी

4
@chovy "फ़ाइल डिस्क्रिप्टर", एक पूर्णांक हैंडल एक खुली फाइल को नामित करता है।
एलेक्स बी

6
अगर किसी और को आश्चर्य हो रहा है: वाक्यविन्यास के ( command A ) command Bलिए एक उपखंड को आमंत्रित करता है command ATldp.org/LDP/abs/html/subshells.html पर प्रलेखित । मैं अभी भी
उप-समूह

1
मुझे लगता है कि उप-शेल के अंदर का कोड अधिक होना चाहिए: if flock -x -w 10 200; then ...Do stuff...; else echo "Failed to lock file" 1>&2; fiताकि यदि टाइमआउट होता है (किसी अन्य प्रक्रिया में फ़ाइल लॉक है), तो यह स्क्रिप्ट आगे नहीं जाती है और फ़ाइल को संशोधित करती है। संभवतः ... प्रतिवाद 'है, लेकिन अगर इसमें 10 सेकंड लगे हैं और ताला अभी भी उपलब्ध नहीं है, तो यह कभी भी उपलब्ध नहीं होगा', संभवत: क्योंकि ताला धारण करने वाली प्रक्रिया समाप्त नहीं हो रही है (शायद इसे चलाया जा रहा है) डिबगर के तहत?)
जोनाथन लेफ्लर

1
जिस फ़ाइल पर लॉक किया गया है, वह केवल उस कार्य को करने के लिए एक स्थान पर फोल्डर है, जिसमें कोई सार्थक डेटा नहीं है। के exitअंदर के भाग से है ( )। जब उपप्रकार समाप्त हो जाता है, तो लॉक स्वचालित रूप से जारी होता है, क्योंकि इसे धारण करने की कोई प्रक्रिया नहीं है।
क्लैक

158

"लॉक फाइल्स" के अस्तित्व का परीक्षण करने वाले सभी दृष्टिकोण त्रुटिपूर्ण हैं।

क्यों? क्योंकि यह जांचने का कोई तरीका नहीं है कि क्या कोई फ़ाइल मौजूद है और इसे एक एकल परमाणु क्रिया में बनाएँ। होने के कारण; वहाँ एक दौड़ की स्थिति है कि आपसी बहिष्करण विराम पर आपके प्रयास करेंगे

इसके बजाय, आपको उपयोग करने की आवश्यकता है mkdirmkdirएक निर्देशिका बनाता है अगर यह अभी तक मौजूद नहीं है, और यदि ऐसा होता है, तो यह एक निकास कोड सेट करता है। इससे भी महत्वपूर्ण बात, यह एक एकल परमाणु क्रिया में यह सब करता है जिससे यह इस परिदृश्य के लिए एकदम सही है।

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

सभी विवरणों के लिए, उत्कृष्ट BashFAQ देखें: http://mywiki.wooledge.org/BashFAQ/045

यदि आप बासी ताले की देखभाल करना चाहते हैं, तो फ्यूज़र (1) काम में आता है। यहाँ केवल नकारात्मक पक्ष यह है कि ऑपरेशन में लगभग एक सेकंड लगता है, इसलिए यह तत्काल नहीं है।

यहाँ एक समारोह है जो मैंने एक बार लिखा था कि फ्यूज़र का उपयोग करके समस्या हल करता है:

#       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 
}

आप इसे स्क्रिप्ट में उपयोग कर सकते हैं जैसे:

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

यदि आप पोर्टेबिलिटी के बारे में परवाह नहीं करते हैं (इन समाधानों को किसी भी यूनिक्स बॉक्स पर बहुत अधिक काम करना चाहिए), लिनक्स ' फ्यूज़र (1) कुछ अतिरिक्त विकल्प प्रदान करता है और झुंड (1) भी है


1
आप इस if ! mkdirहिस्से की जांच के साथ जोड़ सकते हैं कि क्या लॉकड के अंदर संग्रहीत पीआईडी ​​के साथ प्रक्रिया (सक्सेसर स्टार्टअप पर) वास्तव में चल रही है और स्टैलनस सुरक्षा के लिए स्क्रिप्ट के समान है। यह रिबूट के बाद पीआईडी ​​का पुन: उपयोग करने से भी बचाएगा, और इसकी आवश्यकता भी नहीं है fuser
टोबियास किंजलर

4
यह निश्चित रूप से सच है कि परमाणु ऑपरेशन के रूप में परिभाषितmkdir नहीं किया गया है और जैसे कि "साइड-इफेक्ट" फाइल सिस्टम का कार्यान्वयन विवरण है। मुझे पूरा विश्वास है कि अगर वह कहते हैं कि एनएफएस इसे परमाणु फैशन में लागू नहीं करता है। हालांकि मुझे संदेह नहीं है कि आपका /tmpएनएफएस हिस्सा होगा और संभवतः एफ़एस द्वारा लागू किए जाने वाले एफएस द्वारा प्रदान किया जाएगा mkdir
लहनाथ

5
लेकिन एक नियमित फ़ाइल के अस्तित्व की जांच करने और इसे परमाणु रूप से बनाने का एक तरीका है यदि ऐसा नहीं होता है: lnकिसी अन्य फ़ाइल से हार्ड लिंक बनाने के लिए उपयोग करना। यदि आपके पास अजीब फाइल सिस्टम हैं जो गारंटी नहीं देते हैं, तो आप नई फाइल के इनकोड को बाद में यह देखने के लिए देख सकते हैं कि यह मूल फाइल के समान है या नहीं।
जुआन सेस्पीड्स

4
वहाँ है यह है - 'एक फ़ाइल मौजूद है या नहीं की जाँच करें और एकल परमाणु कार्रवाई में इसे बनाने के लिए एक तरह से' open(... O_CREAT|O_EXCL)। आपको ऐसा करने के लिए बस एक उपयुक्त उपयोगकर्ता कार्यक्रम की आवश्यकता है, जैसे lockfile-create(में lockfile-progs) या dotlockfile(में liblockfile-bin)। और सुनिश्चित करें कि आप ठीक से सफाई (जैसे trap EXIT), या बासी ताले के लिए परीक्षण (जैसे के साथ --use-pid)।
टोबी स्पाईट

5
"सभी दृष्टिकोण जो" लॉक फाइलों "के अस्तित्व का परीक्षण करते हैं, त्रुटिपूर्ण हैं। क्यों? क्योंकि यह जांचने का कोई तरीका नहीं है कि क्या कोई फ़ाइल मौजूद है और इसे एक एकल परमाणु क्रिया में बनाते हैं।" - इसे परमाणु बनाने के लिए इसे करना होगा। कर्नेल स्तर - और इसे कर्नेल स्तर पर झुंड (1) linux.die.net/man/1/flock के साथ किया जाता है, जो कम से कम 2006 के बाद से पास होने के लिए आदमी कॉपीराइट तिथि से प्रकट होता है। इसलिए मैंने एक डाउनवोट बनाया (- 1), व्यक्तिगत कुछ भी नहीं, बस दृढ़ विश्वास है कि कर्नेल डेवलपर्स द्वारा प्रदान किए गए कर्नेल कार्यान्वित टूल का उपयोग करना सही है।
क्रेग हिक्स

42

झुंड (2) सिस्टम कॉल के आसपास एक आवरण है, जिसे अकल्पनीय रूप से झुंड (1) कहा जाता है। यह अपेक्षाकृत आसानी से सफाई के बारे में चिंता किए बिना अनन्य ताले प्राप्त करना आसान बनाता है आदि । मैन पेज पर उदाहरण हैं कि इसे शेल स्क्रिप्ट में कैसे उपयोग किया जाए।


3
flock()सिस्टम कॉल POSIX नहीं है और एनएफएस माउंट पर फ़ाइलों के लिए काम नहीं करता।
मैक्सचेलपिजिग

17
एक क्रोन नौकरी से भागना मैं flock -x -n %lock file% -c "%command%"यह सुनिश्चित करने के लिए उपयोग करता हूं कि केवल एक उदाहरण कभी निष्पादित हो रहा है।
रयॉल

ए वी, अकल्पनीय झुंड (1) के बजाय उन्हें झुंड (यू) जैसी किसी चीज के साथ जाना चाहिए था। .. .इसका कुछ परिचित है। । .seems जैसा मैंने सुना है कि एक या दो समय से पहले।
केंट क्रुकबर्ग

यह उल्लेखनीय है कि झुंड (2) प्रलेखन केवल फाइलों के साथ उपयोग को निर्दिष्ट करता है, लेकिन झुंड (1) प्रलेखन या तो फ़ाइल या निर्देशिका के साथ उपयोग को निर्दिष्ट करता है। झुंड (1) प्रलेखन स्पष्ट नहीं है कि निर्माण के दौरान अंतर को कैसे इंगित किया जाए, लेकिन मुझे लगता है कि यह अंतिम "/" जोड़कर किया जाता है। वैसे भी, अगर झुंड (1) निर्देशिकाओं को संभाल सकता है, लेकिन झुंड (2) नहीं कर सकता है, तो झुंड (1) केवल झुंड (2) पर लागू नहीं किया जाता है।
क्रेग हिक्स

27

आपको एक परमाणु संचालन की आवश्यकता है, जैसे झुंड, अन्यथा यह अंततः विफल हो जाएगा।

लेकिन अगर झुंड उपलब्ध नहीं है तो क्या करें। वैसे तो मुक्दिर है। वह एक परमाणु ऑपरेशन भी है। केवल एक प्रक्रिया के परिणामस्वरूप एक सफल mkdir होगा, अन्य सभी विफल हो जाएंगे।

तो कोड है:

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

आपको बासी तालों की देखभाल करने की आवश्यकता है, एक और दुर्घटना से आपकी स्क्रिप्ट फिर कभी नहीं चलेगी।


1
इसे कुछ बार समवर्ती रूप से चलाएं (जैसे "./a.sh &/a.sh & ./a.sh &/a.sh &//a.sh &/a.sh &/a.sh" & ") और स्क्रिप्ट कुछ ही समय में लीक हो जाएगी।
निपसीसोरस

7
@ निप्पिसॉरस: यह लॉकिंग विधि लीक नहीं होती है। आपने जो देखा, वह सभी प्रतियों को लॉन्च करने से पहले समाप्त करने वाली प्रारंभिक स्क्रिप्ट थी, इसलिए एक और लॉक को सही ढंग से प्राप्त करने में सक्षम था। इस झूठे सकारात्मक से बचने के लिए, sleep 10पहले जोड़ें rmdirऔर फिर से कैस्केड करने का प्रयास करें - कुछ भी "लीक" नहीं होगा।
सर एथोस

अन्य स्रोत दावा करते हैं कि एनएफएस जैसे कुछ फाइल सिस्टम पर mkdir परमाणु नहीं है। और btw मैंने ऐसे मौके देखे हैं जहाँ NFS समवर्ती पुनरावर्ती mkdir पर त्रुटि होती है कभी-कभी जेनकिंस मैट्रिक्स नौकरियों के साथ। इसलिए मुझे पूरा यकीन है कि मामला यही है। लेकिन कम मांग वाले मामलों के IMO के लिए mkdir बहुत अच्छा है।
अकोस्टैडिनोव

आप नियमित फ़ाइलों के साथ Bash'es noclobber विकल्प का उपयोग कर सकते हैं।
पलक

26

लॉकिंग को विश्वसनीय बनाने के लिए आपको एक परमाणु ऑपरेशन की आवश्यकता होती है। उपरोक्त कई प्रस्ताव परमाणु नहीं हैं। प्रस्तावित लॉकफाइल (1) उपयोगिता के रूप में आशाजनक दिख रहा है, जैसा कि उल्लेख किया गया है कि इसका "एनएफएस-प्रतिरोधी" है। यदि आपका OS लॉकफ़ाइल (1) का समर्थन नहीं करता है और आपके समाधान को NFS पर काम करना है, तो आपके पास कई विकल्प नहीं हैं ...।

NFSv2 में दो परमाणु ऑपरेशन हैं:

  • सिमलिंक
  • नाम बदलने

NFSv3 के साथ क्रिएट कॉल भी परमाणु है।

निर्देशिका संचालन NFSv2 और NFSv3 के तहत परमाणु नहीं हैं (ब्रेंट कॉलगान, आईएसबीएन 0-201-32570-5 द्वारा पुस्तक 'एनएफएस इलस्ट्रेटेड' के लिए कृपया देखें; ब्रेंट सन में एनएफएस-अनुभवी है)।

यह जानकर, आप फ़ाइलों और निर्देशिकाओं के लिए स्पिन-लॉक लागू कर सकते हैं (शेल में, PHP नहीं):

ताला वर्तमान dir:

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

फ़ाइल लॉक करें:

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

वर्तमान dir अनलॉक करें (धारणा, चल रही प्रक्रिया ने वास्तव में लॉक का अधिग्रहण किया):

mv lock deleteme && rm deleteme

एक फ़ाइल अनलॉक करें (धारणा, चल रही प्रक्रिया ने वास्तव में लॉक का अधिग्रहण किया):

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

निकालें भी परमाणु नहीं है, इसलिए पहले नाम (जो परमाणु है) और फिर निकालें।

सिम्लिंक और रीनेम कॉल के लिए, दोनों फाइलनेम को एक ही फाइल सिस्टम पर रहना होता है। मेरा प्रस्ताव: केवल सरल फ़ाइल नाम (कोई पथ नहीं) का उपयोग करें और एक ही निर्देशिका में फ़ाइल और लॉक डालें।


एनएफएस इलस्ट्रेटेड के कौन से पृष्ठ इस कथन का समर्थन करते हैं कि एमकेडीआर एनएफएस से अधिक परमाणु नहीं है?
मैकशलेपजिग

इस तकनीक के लिए Thnks। एक शेल म्यूटेक्स कार्यान्वयन मेरे नए शेल लिब में उपलब्ध है: github.com/Offirmo/offirmo-shell-lib , "mutex" देखें। यह lockfileउपलब्ध है या symlinkनहीं तो इस पद्धति का उपयोग करता है यदि नहीं।
ऑफिरमो

अच्छा लगा। दुर्भाग्य से यह विधि बासी ताले को स्वचालित रूप से हटाने का एक तरीका प्रदान नहीं करती है।
रिचर्ड हैनसेन

दो प्रक्रियाओं अनलॉक mv, ( rm), के rm -fलिए इस्तेमाल किया जाना चाहिए, बजाय rmदो प्रक्रियाओं P1, P2 रेसिंग कर रहे हैं? उदाहरण के लिए, पी 1 शुरू होता है mv, फिर पी 2 लॉक, फिर पी 2 अनलॉक (दोनों mvऔर rm), आखिरकार पी 1 प्रयास rmविफल हो जाता है।
मैट वालिस

1
@MattWallis फ़ाइल नाम $$में शामिल करके अंतिम समस्या को आसानी से कम किया जा सकता है ${f}.deleteme
स्टीफन माजूस्की 12

23

एक अन्य विकल्प शेल के noclobberविकल्प को चलाकर उपयोग करना है set -C>यदि फ़ाइल पहले से मौजूद है तो फिर विफल हो जाएगी।

संक्षेप में:

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

यह शेल को कॉल करने का कारण बनता है:

open(pathname, O_CREAT|O_EXCL)

यदि एटमॉली फ़ाइल बनाता है या विफल रहता है यदि फ़ाइल पहले से मौजूद है।


BashFAQ 045 पर एक टिप्पणी के अनुसार , यह विफल हो सकता है ksh88, लेकिन यह मेरे सभी गोले में काम करता है:

$ 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

दिलचस्प है कि झंडा pdkshजोड़ता है O_TRUNC, लेकिन जाहिर है यह बेमानी है:
या तो आप एक खाली फ़ाइल बना रहे हैं, या आप कुछ भी नहीं कर रहे हैं।


आप कैसे करते हैं यह इस rmबात पर निर्भर करता है कि आप कैसे अशुद्ध बाहर निकालना चाहते हैं।

स्वच्छ निकास पर हटाएं

नया रन तब तक विफल रहता है जब तक कि अशुद्ध निकास हल न हो जाए और लॉकफ़ाइल मैन्युअल रूप से हटा दिए जाने के कारण समस्या न हो।

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

किसी भी निकास पर हटाएं

नए रन सफल रहे बशर्ते स्क्रिप्ट पहले से न चल रही हो।

trap 'rm "$lockfile"' EXIT

बहुत उपन्यास दृष्टिकोण ... यह एक ताला निर्देशिका के बजाय एक लॉक फ़ाइल का उपयोग करके परमाणुता को पूरा करने का एक तरीका प्रतीत होता है।
मैट कैलडवेल

अच्छा तरीका। :-) EXIT ट्रैप पर, यह प्रतिबंधित होना चाहिए कि कौन सी प्रक्रिया लॉक फाइल को साफ कर सकती है। उदाहरण के लिए: जाल 'अगर [[$ (बिल्ली "$ लॉकफाइल") == "" $ $ "]]; फिर आरएम "$ लॉकफ़ाइल"; Fi 'EXIT
केविन सीफर्ट

1
लॉक फ़ाइलें NFS पर परमाणु नहीं हैं। यही कारण है कि लोग लॉक निर्देशिकाओं का उपयोग करने के लिए चले गए।
के रिचर्ड पिक्ले

20

आप इसके लिए उपयोग कर सकते हैं GNU Parallelक्योंकि यह म्यूटेक्स के रूप में काम करता है जब इसे बुलाया जाता है sem। तो, ठोस शब्दों में, आप उपयोग कर सकते हैं:

sem --id SCRIPTSINGLETON yourScript

यदि आप एक टाइमआउट भी चाहते हैं, तो उपयोग करें:

sem --id SCRIPTSINGLETON --semaphoretimeout -10 yourScript

<0 का टाइमआउट का मतलब है बिना रनिंग स्क्रिप्ट के बाहर निकलना अगर सेमआउट को टाइमआउट के भीतर जारी नहीं किया जाता है, तो 0 का टाइमआउट वैसे भी स्क्रिप्ट को रन करता है।

ध्यान दें कि आपको इसे एक नाम देना चाहिए (साथ में --id) यह नियंत्रण टर्मिनल के लिए चूक है।

GNU Parallel अधिकांश लिनक्स / ओएसएक्स / यूनिक्स प्लेटफार्मों पर एक बहुत ही सरल इंस्टॉल है - यह सिर्फ एक पर्ल स्क्रिप्ट है।


बहुत बुरे लोग बेकार उत्तरों को कम करने के लिए अनिच्छुक होते हैं: इससे नए प्रासंगिक उत्तर रद्दी के ढेर में दब जाते हैं।
दिमित्री ग्रिगोरीव

4
हमें बस बहुत सारे अपवोट चाहिए। यह एक स्पष्ट और अल्पज्ञात उत्तर है। (हालांकि पांडित्यपूर्ण ओपी जल्दी-और-गंदा चाहता था, जबकि यह त्वरित और स्वच्छ है!) semसंबंधित प्रश्न unix.stackexchange.com/a/322200/199525 पर अधिक ।
आंशिक रूप से बादल छाए रहेंगे

16

शेल स्क्रिप्ट के लिए, मैं mkdirओवर के साथ जाना पसंद करता हूं flockक्योंकि यह तालों को अधिक पोर्टेबल बनाता है।

किसी भी तरह से, का उपयोग set -eकरना पर्याप्त नहीं है। यदि कोई आदेश विफल रहता है, तो केवल स्क्रिप्ट से बाहर निकलता है। आपके ताले अभी भी पीछे रह जाएंगे।

उचित लॉक क्लीनअप के लिए, आपको वास्तव में अपना जाल इस तरह के पसिडो कोड (उठा हुआ, सरलीकृत और अप्रयुक्त लेकिन सक्रिय रूप से उपयोग की जाने वाली स्क्रिप्ट से) के लिए सेट करना चाहिए:

#=======================================================================
# 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

यहाँ क्या होगा। सभी जाल एक निकास का उत्पादन करेंगे ताकि फ़ंक्शन __sig_exitहमेशा होगा (एक साइकल को छोड़कर) जो आपके ताले को साफ करता है।

नोट: मेरे निकास मान निम्न मान नहीं हैं। क्यों? विभिन्न बैच प्रोसेसिंग सिस्टम 31 के माध्यम से संख्या 0 की अपेक्षाएं रखते हैं या करते हैं। उन्हें कुछ और के लिए सेट करना, मैं अपनी स्क्रिप्ट और बैच स्ट्रीम पिछले बैच की नौकरी या स्क्रिप्ट के अनुसार प्रतिक्रिया कर सकता हूं।


2
आपकी पटकथा भी बहुत क्रियात्मक है, मुझे लगता है कि बहुत कम हो सकता है, लेकिन कुल मिलाकर, हां, आपको इसे सही ढंग से करने के लिए जाल स्थापित करना होगा। इसके अलावा मैं SITEUP जोड़ूंगा।
मौजुबा

यह अच्छी तरह से काम करता है, सिवाय यह $ LOCK_DIR के लिए जाँच करने के लिए लगता है जबकि यह $ __ लॉकडीर को हटा देता है। शायद मुझे सुझाव देना चाहिए कि ताला हटाते समय आप rm -r $ LOCK_DIR करेंगे?
bevada

सुझाव के लिए धन्यवाद। इसके बाद के संस्करण को उठा लिया गया था और इसे एक psuedo कोड फैशन में रखा गया था, इसलिए इसे लोगों के उपयोग के आधार पर ट्यूनिंग की आवश्यकता होगी। हालांकि, मैं जानबूझ कर मेरे मामले में rmdir साथ चला गया के रूप में rmdir सुरक्षित रूप से निर्देशिका को हटा only_if वे खाली हैं। अगर लोग उनमें संसाधन जैसे पीआईडी ​​फाइलें आदि डाल रहे हैं, तो उन्हें अपने लॉक क्लीनअप को अधिक आक्रामक rm -r $LOCK_DIRरूप से बदलना चाहिए या इसे आवश्यक रूप से लागू करना चाहिए (जैसा कि मैंने विशेष मामलों में भी किया है जैसे रिश्तेदार स्कैच फाइल को पकड़े हुए)। चीयर्स।
मार्क स्टिन्सन

क्या आपने परीक्षण किया है exit 1002?
गाइल्स क्वेनोट

13

वास्तव में जल्दी और वास्तव में गंदा? आपकी स्क्रिप्ट के शीर्ष पर यह एक-लाइनर काम करेगा:

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

बेशक, बस यह सुनिश्चित कर लें कि आपका स्क्रिप्ट नाम अद्वितीय है। :)


मैं इसका परीक्षण करने के लिए इसका अनुकरण कैसे करूं? क्या एक लाइन में दो बार स्क्रिप्ट शुरू करने का तरीका है और शायद पहले से ही चल रहा है, तो चेतावनी मिल सकती है?
रुब77 7

2
यह बिल्कुल काम नहीं कर रहा है! जांच क्यों -gt 2? grep हमेशा ps के परिणाम में खुद को नहीं पाता है!
रुबो77

pgrepPOSIX में नहीं है। यदि आप इसे कार्यशील रूप से प्राप्त करना चाहते हैं, तो आपको POSIX की आवश्यकता है psऔर इसके आउटपुट की प्रक्रिया करें।
पालेक

OSX पर -cमौजूद नहीं है, आपको उपयोग करना होगा | wc -l। संख्या की तुलना के बारे में: -gt 1पहला उदाहरण खुद को देखने के बाद से जाँच की जाती है।
बेंजामिन पीटर

6

यहां एक दृष्टिकोण है जो पीआईडी ​​के माध्यम से बासी लॉक के लिए एक चेक के साथ परमाणु निर्देशिका लॉकिंग को जोड़ती है और यदि बासी हो तो पुनरारंभ करें। इसके अलावा, यह किसी भी bashism पर निर्भर नहीं करता है।

#!/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

एक ज्ञात स्थान पर एक लॉक फ़ाइल बनाएं और स्क्रिप्ट शुरू होने पर अस्तित्व की जांच करें? यदि किसी स्क्रिप्ट के निष्पादन को रोकने वाले किसी गलत उदाहरण को ट्रैक करने का प्रयास कर रहा हो, तो फ़ाइल में PID डालना मददगार हो सकता है।


5

इस उदाहरण को मानव झुंड में समझाया गया है, लेकिन इसके लिए कुछ आवेगों की आवश्यकता है, क्योंकि हमें बग और निकास कोड का प्रबंधन करना चाहिए:

   #!/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

आप एक अन्य विधि, सूची प्रक्रियाओं का उपयोग कर सकते हैं जो मैंने अतीत में उपयोग किया था। लेकिन यह उस विधि से अधिक जटिल है। आपको पीएस द्वारा प्रक्रियाओं को सूचीबद्ध करना चाहिए, इसके नाम से फ़िल्टर करना चाहिए, परजीवी नाड को हटाने के लिए अतिरिक्त फ़िल्टर grep -v grep अंत में इसे grep -c द्वारा गिनना चाहिए। और संख्या के साथ तुलना करें। इसकी जटिल और अनिश्चित है


1
आप ln -s का उपयोग कर सकते हैं, क्योंकि यह केवल तभी कोई फ़ाइल या सिमलिंक मौजूद है, जो mkdir के रूप में मौजूद है सिम्लिंक बना सकता है। सिस्टम की बहुत सारी प्रक्रियाएँ पूर्व में सीमलिंक का उपयोग करती थीं, उदाहरण के लिए init या inetd। synlink प्रक्रिया आईडी रखता है, लेकिन वास्तव में कुछ नहीं करने के लिए इंगित करता है। वर्षों से इस व्यवहार को बदल दिया गया था। प्रक्रियाएं झुंड और अर्धचंद्र का उपयोग करती हैं।
ज़निक

5

पोस्ट किए गए मौजूदा उत्तर या तो सीएलआई उपयोगिता पर भरोसा करते हैं flockया लॉक फ़ाइल को ठीक से सुरक्षित नहीं करते हैं। झुंड की उपयोगिता सभी गैर-लिनक्स प्रणालियों (यानी फ्रीबीएसडी) पर उपलब्ध नहीं है, और एनएफएस पर ठीक से काम नहीं करता है।

सिस्टम एडमिनिस्ट्रेशन और सिस्टम डेवलपमेंट के अपने शुरुआती दिनों में, मुझे बताया गया था कि लॉक फाइल बनाने की एक सुरक्षित और अपेक्षाकृत पोर्टेबल विधि थी टेम्परेरी फाइल का उपयोग करना mkemp(3)या mkemp(1), टेम्प फाइल (यानी PID) की जानकारी लिखना, फिर हार्ड लिंक। अस्थायी फ़ाइल को लॉक फ़ाइल में। यदि लिंक सफल था, तो आपने सफलतापूर्वक लॉक प्राप्त कर लिया है।

शेल स्क्रिप्ट में ताले का उपयोग करते समय, मैं आमतौर पर obtain_lock()एक साझा प्रोफ़ाइल में एक फ़ंक्शन रखता हूं और फिर इसे स्क्रिप्ट से स्रोत बनाता हूं । नीचे मेरे लॉक फ़ंक्शन का एक उदाहरण है:

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;
};

निम्नलिखित ताला फ़ंक्शन का उपयोग करने का एक उदाहरण है:

#!/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

clean_upअपनी स्क्रिप्ट में किसी भी निकास बिंदु पर कॉल करना याद रखें ।

मैंने उपरोक्त दोनों लिनक्स और फ्रीबीएसडी वातावरण में उपयोग किया है।


4

डेबियन मशीन को लक्षित करते समय मुझे lockfile-progsपैकेज एक अच्छा समाधान लगता है। procmailएक lockfileटूल भी आता है । हालाँकि कभी-कभी मैं इनमें से किसी के साथ फंस जाता हूं।

यहाँ मेरा समाधान है जो mkdirबासी तालों का पता लगाने के लिए परमाणु-नेस और एक पीआईडी ​​फ़ाइल का उपयोग करता है । यह कोड वर्तमान में Cygwin सेटअप पर उत्पादन में है और अच्छी तरह से काम करता है।

इसका उपयोग करने के लिए बस exclusive_lock_requireउस समय कॉल करें जब आपको किसी चीज़ की अनन्य पहुँच की आवश्यकता हो। एक वैकल्पिक लॉक नाम पैरामीटर आपको विभिन्न स्क्रिप्ट के बीच ताले साझा करने देता है। दो निचले स्तर के कार्य भी हैं ( exclusive_lock_tryऔर 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
}

धन्यवाद, इसे खुद साइबरविन पर आजमाया और इसने सरल परीक्षण पास किए।
ndemou

4

यदि झुंड की सीमाएं, जो पहले से ही इस धागे पर कहीं और वर्णित की गई हैं, आपके लिए कोई समस्या नहीं हैं, तो यह काम करना चाहिए:

#!/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
बस मैंने सोचा था कि x (लॉक लिखना) पहले से ही डिफ़ॉल्ट रूप से सेट है।
क्लेडॉन अल्लेनेई

और -nजाएगा exit 1तुरंत अगर यह ताला नहीं मिल सकता है
Anentropic

धन्यवाद @KeldonAlleyne, मैंने कोड को "-x" हटाने के लिए अद्यतन किया है क्योंकि यह डिफ़ॉल्ट है।
presto8

3

कुछ यूनिक्स lockfileमें पहले से ही उल्लेख के समान है flock

मैनपेज से:

लॉकफाइल का उपयोग एक या अधिक सेमाफोर फाइलें बनाने के लिए किया जा सकता है। यदि लॉक-फ़ाइल सभी निर्दिष्ट फ़ाइलों (निर्दिष्ट क्रम में) को नहीं बना सकती है, तो यह स्लीपटाइम (8 से डिफॉल्ट) सेकंड इंतजार करती है और अंतिम फ़ाइल को पुनर्प्राप्त करती है जो सफल नहीं हुई। जब तक विफलता वापस नहीं हो जाती तब तक आप रिट्रीट की संख्या निर्दिष्ट कर सकते हैं। यदि रिट्रीज़ की संख्या -1 है (डिफ़ॉल्ट, यानी -r-1) लॉकफाइल हमेशा के लिए पुनः प्रयास करेगा।


हम lockfileउपयोगिता कैसे प्राप्त करते हैं ??
ऑफिशो

lockfileके साथ वितरित किया जाता है procmail। इसके अलावा एक विकल्प है dotlockfileजो liblockfileपैकेज के साथ जाता है। वे दोनों एनएफएस पर मज़बूती से काम करने का दावा करते हैं।
श्री डेथलेस

3

असल में यद्यपि बम्हैड्स का उत्तर लगभग अच्छा है, फिर भी पहली बार लॉकफ़ाइल की जाँच करने और इसे लिखने से पहले दूसरी स्क्रिप्ट को चलाने का थोड़ा मौका है। इसलिए वे दोनों लॉक फाइल को लिखेंगे और दोनों चलेंगे। यहाँ यह सुनिश्चित करने के लिए काम करना है:

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

noclobberविकल्प यकीन है कि अगर फ़ाइल पहले से मौजूद रीडायरेक्ट आदेश विफल हो जाएगा कर देगा। इसलिए रीडायरेक्ट कमांड वास्तव में परमाणु है - आप फ़ाइल को एक कमांड के साथ लिखते और चेक करते हैं। आपको फ़ाइल के अंत में लॉकफाइल को हटाने की आवश्यकता नहीं है - यह जाल द्वारा हटा दिया जाएगा। मुझे उम्मीद है कि इससे लोगों को मदद मिलेगी जो इसे बाद में पढ़ेंगे।

PS मैंने यह नहीं देखा कि मिकेल ने पहले ही प्रश्न का सही उत्तर दे दिया है, हालांकि उसने उदाहरण के लिए Ctrl-C के साथ स्क्रिप्ट को रोकने के बाद लॉक फ़ाइल को छोड़ दिया जाएगा मौका को कम करने के लिए ट्रैप कमांड को शामिल नहीं किया। तो यह पूरा समाधान है


3

मैं लॉकफाइल्स, लॉकडायर, विशेष लॉकिंग प्रोग्राम और यहां तक ​​कि pidofजब से यह सभी लिनक्स इंस्टॉलेशन पर नहीं मिला है , तब भी मैं इसे दूर करना चाहता था । इसके अलावा सरलतम संभव कोड (या संभव के रूप में कम से कम कुछ पंक्तियाँ) चाहते थे। सरलतम ifकथन, एक पंक्ति में:

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

1
यह 'ps' आउटपुट के लिए संवेदनशील है, मेरी मशीन (Ubuntu 14.04, / bin / ps from procps-ng संस्करण 3.3.9) पर 'ps axf' कमांड ascii ट्री अक्षर प्रिंट करता है जो फील्ड नंबरों को बाधित करता है। इसने मेरे लिए काम किया: /bin/ps -a --format pid,cmd | awk -v pid=$$ '/'$(basename $0)'/ { if ($1!=pid) print $1; }'
19

2

मैं एक सरल दृष्टिकोण का उपयोग करता हूं जो बासी लॉक फ़ाइलों को संभालता है।

ध्यान दें कि उपरोक्त कुछ समाधान जो पीआईडी ​​को स्टोर करते हैं, इस तथ्य को अनदेखा करते हैं कि पीआईडी ​​चारों ओर लपेट सकता है। इसलिए - सिर्फ जाँच करना कि क्या संग्रहित पीड के साथ एक वैध प्रक्रिया पर्याप्त नहीं है, विशेष रूप से लंबे समय तक चलने वाली स्क्रिप्ट के लिए।

मैं noclobber का उपयोग यह सुनिश्चित करने के लिए करता हूं कि केवल एक स्क्रिप्ट खोल सकता है और एक समय में लॉक फ़ाइल को लिख सकता है। इसके अलावा, मैं लॉकफ़ाइल में एक प्रक्रिया को विशिष्ट रूप से पहचानने के लिए पर्याप्त जानकारी संग्रहीत करता हूं। मैं डेटा के सेट को विशिष्ट रूप से pid, ppid, lstart होने की प्रक्रिया की पहचान करता हूं।

जब कोई नई स्क्रिप्ट शुरू होती है, अगर वह लॉक फ़ाइल बनाने में विफल रहती है, तो यह सत्यापित करता है कि लॉक फ़ाइल बनाने वाली प्रक्रिया अभी भी आस-पास है। यदि नहीं, तो हम मानते हैं कि मूल प्रक्रिया एक असामयिक मृत्यु हो गई, और एक बासी ताला फ़ाइल को छोड़ दिया। नई स्क्रिप्ट तब लॉक फ़ाइल का स्वामित्व लेती है, और सभी अच्छी तरह से दुनिया है, फिर से।

कई प्लेटफार्मों में कई गोले के साथ काम करना चाहिए। तेज, पोर्टेबल और सरल।

#!/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

मैंने आपको एक अलग समाधान के लिए +1 दिया। Althoug यह AIX (> ps -eo pid, ppid, lstart $$; -o -1 ps: -o के साथ अमान्य सूची) में काम नहीं करता है। HP-UX (> ps -eo pid, ppid, lstart $$) | टेल -1 पीएस: अवैध विकल्प - ओ)। धन्यवाद।
तगार

2

इस लाइन को अपनी स्क्रिप्ट की शुरुआत में जोड़ें

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

यह आदमी झुंड से एक बॉयलरप्लेट कोड है।

यदि आप अधिक लॉगिंग चाहते हैं, तो इसका उपयोग करें

[ "${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."

यह सेट और चेक लॉक का उपयोग करता है flock यूटिलिटी । यह कोड पता लगाता है कि अगर यह पहली बार FLOCKER वैरिएबल की जाँच करके चलाया गया था, यदि यह स्क्रिप्ट नाम पर सेट नहीं है, तो यह फिर से झुंड का उपयोग करके स्क्रिप्ट को फिर से शुरू करने की कोशिश करता है और FLOCKER वैरिएबल को इनिशियलाइज़ किया जाता है, यदि FLOCKER को सही तरीके से सेट किया जाता है, तो पिछले पुनरावृत्ति पर झुंड सफल हुआ और आगे बढ़ना ठीक है। यदि लॉक व्यस्त है, तो यह कॉन्फ़िगर करने योग्य निकास कोड के साथ विफल हो जाता है।

ऐसा लगता है कि डेबियन 7 पर काम नहीं किया जा रहा है, लेकिन प्रायोगिक उपयोग-लाइन 2.25 पैकेज के साथ फिर से काम करने लगता है। यह लिखता है "झुंड: ... पाठ फ़ाइल व्यस्त"। आपकी स्क्रिप्ट पर लिखने की अनुमति को अक्षम करने से इसे ओवरराइड किया जा सकता है।


1

पीआईडी ​​और लॉकफाइल्स निश्चित रूप से सबसे विश्वसनीय हैं। जब आप प्रोग्राम को चलाने का प्रयास करते हैं, तो यह लॉकफ़ाइल के लिए जाँच कर सकता है जो मौजूद है और यदि मौजूद है, तो यह psदेखने के लिए उपयोग कर सकता है कि क्या प्रक्रिया अभी भी चल रही है। यदि यह नहीं है, तो स्क्रिप्ट शुरू हो सकती है, पीआईडी ​​को लॉकफाइल में अपडेट कर सकता है।


1

मुझे लगता है कि bmdhack का समाधान सबसे व्यावहारिक है, कम से कम मेरे उपयोग के मामले के लिए। झुंड और लॉकफाइल का उपयोग करते हुए स्क्रिप्ट समाप्त होने पर आरएम का उपयोग करके लॉकफाइल को हटाने पर भरोसा करते हैं, जिसे हमेशा गारंटी नहीं दी जा सकती है (जैसे, किल -9)।

मैं bmdhack के समाधान के बारे में एक छोटी सी बात को बदल दूंगा: यह लॉक फाइल को हटाने का एक बिंदु बनाता है, यह बताते हुए कि यह इस सेमाफोर के सुरक्षित काम के लिए अनावश्यक है। किल -0 का उनका उपयोग यह सुनिश्चित करता है कि एक मृत प्रक्रिया के लिए एक पुरानी तालाबंदी को केवल अनदेखा / अधिक लिखा जाएगा।

मेरा सरलीकृत समाधान इसलिए है कि बस अपने सिंगनल के शीर्ष पर निम्नलिखित को जोड़ें:

## 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}

बेशक, इस स्क्रिप्ट में अभी भी दोष है कि एक ही समय में शुरू होने वाली प्रक्रियाओं में एक दौड़ खतरा है, क्योंकि लॉक टेस्ट और सेट ऑपरेशन एक भी परमाणु कार्रवाई नहीं है। लेकिन mkdir का उपयोग करने के लिए lhunath द्वारा इसके लिए प्रस्तावित समाधान में दोष है कि एक मार दी गई स्क्रिप्ट निर्देशिका को पीछे छोड़ सकती है, इस प्रकार अन्य उदाहरणों को चलने से रोक सकती है।


1

Semaphoric उपयोगिता का उपयोग करता है flock(जैसा कि presto8 से ऊपर चर्चा की, जैसे) एक लागू करने के लिए गिनती सेमाफोर । यह समवर्ती प्रक्रियाओं की किसी भी विशिष्ट संख्या को आप चाहते हैं। हम इसका उपयोग विभिन्न कतार कार्यकर्ता प्रक्रियाओं की समाप्ती के स्तर को सीमित करने के लिए करते हैं।

यह सेम की तरह है लेकिन बहुत हल्का है। (पूर्ण प्रकटीकरण: मैंने लिखा है कि खोजने के बाद सेम हमारी जरूरतों के लिए बहुत भारी था और वहाँ एक सरल गणना अर्ध-उपयोगिता उपलब्ध नहीं थी।)


1

झुंड के साथ एक उदाहरण (1) लेकिन बिना उपधारा के। झुंड () एड फ़ाइल / tmp / foo को कभी भी हटाया नहीं जाता है, लेकिन इससे कोई फर्क नहीं पड़ता क्योंकि यह झुंड () और संयुक्त राष्ट्र का झुंड () एड हो जाता है।

#!/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

पहले से ही एक लाख बार जवाब दिया, लेकिन बाहरी निर्भरता की आवश्यकता के बिना एक और तरीका:

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

हर बार यह वर्तमान पीआईडी ​​($ $) को लॉकफाइल में और स्क्रिप्ट स्टार्टअप चेक पर लिखता है कि क्या कोई प्रक्रिया नवीनतम पीआईडी ​​के साथ चल रही है।


1
ट्रैप कॉल के बिना (या सामान्य मामले के लिए अंत में कम से कम एक सफाई), आपके पास गलत पॉज़िटिव बग है जहां लास्टफाइल को आखिरी रन के बाद चारों ओर छोड़ दिया जाता है और पीआईडी ​​को बाद में किसी अन्य प्रक्रिया द्वारा पुन: उपयोग किया जाता है। (और सबसे बुरी स्थिति में, इसे अपाचे की तरह एक लंबी चलने वाली प्रक्रिया के लिए उपहार में दिया गया है ....)
फिलिप चैनत्रुइल

1
मैं सहमत हूं, मेरा दृष्टिकोण त्रुटिपूर्ण है, इसमें एक जाल की जरूरत है। मैंने अपना समाधान अपडेट कर दिया है। मैं अभी भी बाहरी निर्भरता नहीं करना पसंद करता हूं।
फिलाडोर विसे

1

प्रक्रिया के लॉक का उपयोग करना अधिक मजबूत होता है और असहमतिपूर्ण निकास का भी ध्यान रखता है। जब तक प्रक्रिया चल रही है तब तक lock_file को खुला रखा जाता है। एक बार यह प्रक्रिया बंद हो जाएगी (शेल द्वारा) मौजूद होगी (भले ही यह मार दिया जाए)। मैंने पाया कि यह बहुत कुशल है:

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

मैं स्क्रिप्ट की शुरुआत में oneliner @ का उपयोग करता हूं:

#!/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

मेमोरी में प्रक्रिया की उपस्थिति को देखना अच्छा है (कोई फर्क नहीं पड़ता कि प्रक्रिया की स्थिति क्या है); लेकिन यह मेरे लिए काम करता है।


0

झुंड का रास्ता जाने का रास्ता है। जब स्क्रिप्ट अचानक मर जाती है तो क्या होता है, इसके बारे में सोचें। झुंड-मामले में आप सिर्फ झुंड को ढीला करते हैं, लेकिन यह कोई समस्या नहीं है। इसके अलावा, ध्यान दें कि एक बुरी चाल स्क्रिप्ट पर ही एक झुंड लेना है .. लेकिन निश्चित रूप से आपको अनुमति की समस्याओं में पूर्ण-स्टीम-फॉरवर्ड चलाने की सुविधा मिलती है।


0

जल्दी और गन्दी?

#!/bin/sh

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

..do what you want here..

rm sometempfile

7
यह कैसे उपयोग किया जाता है, इसके आधार पर यह एक मुद्दा हो सकता है या नहीं भी हो सकता है, लेकिन लॉक के लिए परीक्षण और इसे बनाने के बीच एक दौड़ की स्थिति है, ताकि एक ही समय में दो स्क्रिप्ट शुरू हो सकें। यदि एक पहले समाप्त हो जाता है, तो दूसरा बिना लॉक फ़ाइल के साथ चलता रहेगा।
टिम

3
सी न्यूज़, जिसने मुझे पोर्टेबल शेल स्क्रिप्टिंग के बारे में बहुत कुछ सिखाया, एक लॉक बनाने के लिए उपयोग किया गया। $$ फ़ाइल, और फिर इसे "लॉक" के साथ लिंक करने का प्रयास करें - यदि लिंक सुकेसीड, आपके पास लॉक था, अन्यथा आपने लॉक को हटा दिया। $ $। और बाहर निकल गया।
पॉल टॉम्बलिन

यह वास्तव में एक अच्छा तरीका है, सिवाय इसके कि आप अभी भी लॉकफ़ाइल को मैन्युअल रूप से हटाने की आवश्यकता को भुगतना चाहते हैं अगर कुछ गलत हो जाता है और लॉकफ़िल को हटाया नहीं जाता है।
मैथ्यू शार्ले

2
त्वरित और गंदा, कि उसने क्या कहा :)
Aupajo
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.