จะเลือกใช้โซลูชันที่ไม่ต้องการเครื่องมือเพิ่มเติม
ln -s my.pid .lock
จะอ้างสิทธิ์การล็อค (ตามด้วยecho $$ > my.pid
) และเมื่อเกิดข้อผิดพลาดสามารถตรวจสอบว่า PID ที่เก็บไว้ใน.lock
นั้นเป็นอินสแตนซ์ที่ใช้งานจริงของสคริปต์หรือไม่
จะเลือกใช้โซลูชันที่ไม่ต้องการเครื่องมือเพิ่มเติม
ln -s my.pid .lock
จะอ้างสิทธิ์การล็อค (ตามด้วยecho $$ > my.pid
) และเมื่อเกิดข้อผิดพลาดสามารถตรวจสอบว่า PID ที่เก็บไว้ใน.lock
นั้นเป็นอินสแตนซ์ที่ใช้งานจริงของสคริปต์หรือไม่
คำตอบ:
เกือบจะเหมือนคำตอบของ NSG: ใช้ล็อคไดเรกทอรี การสร้างไดเรกทอรีอยู่ภายใต้ลินุกซ์และ unix และ * BSD และระบบปฏิบัติการอื่น ๆ มากมาย
if mkdir $LOCKDIR
then
# Do important, exclusive stuff
if rmdir $LOCKDIR
then
echo "Victory is mine"
else
echo "Could not remove lock dir" >&2
fi
else
# Handle error condition
...
fi
คุณสามารถใส่ PID ของการล็อค sh ลงในไฟล์ในไดเรกทอรีล็อคเพื่อการดีบั๊ก แต่ไม่ตกหลุมพรางของความคิดคุณสามารถตรวจสอบ PID นั้นเพื่อดูว่ากระบวนการล็อคยังดำเนินการอยู่หรือไม่ สภาพการแข่งขันจำนวนมากนอนลงบนเส้นทางนั้น
mkdir
ไม่ได้เป็นอะตอมบน NFS (ซึ่งเป็นกรณีที่ไม่สำหรับผม แต่ผมคิดว่าใครควรจะพูดถึงว่าถ้าเป็นจริง)
/tmp
เป็นที่มักจะไม่ใช้ NFS ร่วมกันดังนั้นควรปรับ
rm -rf
เพื่อลบไดเรกทอรีล็อค rmdir
จะล้มเหลวหากมีคน (ไม่จำเป็นต้องคุณ) จัดการเพื่อเพิ่มไฟล์ลงในไดเรกทอรี
หากต้องการเพิ่มคำตอบของ Bruce Edigerและได้รับแรงบันดาลใจจากคำตอบนี้คุณควรเพิ่มสมาร์ทมากขึ้นในการล้างข้อมูลเพื่อป้องกันการยกเลิกสคริปต์:
#Remove the lock directory
function cleanup {
if rmdir $LOCKDIR; then
echo "Finished"
else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1
fi
}
if mkdir $LOCKDIR; then
#Ensure that if we "grabbed a lock", we release it
#Works for SIGTERM and SIGINT(Ctrl-C)
trap "cleanup" EXIT
echo "Acquired lock, running"
# Processing starts here
else
echo "Could not create lock directory '$LOCKDIR'"
exit 1
fi
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
.
นี่อาจจะง่ายเกินไปโปรดแก้ไขให้ฉันถ้าฉันผิด ไม่ง่ายps
พอใช่ไหม
#!/bin/bash
me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;
# do stuff below this comment
grep -v $$
ใหม่เป็นย่อยของเดิมและได้รับการซ่อนไว้โดย ตัวอย่างจริง: เก่า - 14532, ใหม่ - 1453, เก่า - 28858, ใหม่
grep -v $$
เป็นgrep -v "^${$} "
grep -wv "^$$"
(ดูการแก้ไข)
ฉันจะใช้ไฟล์ล็อคตามที่ระบุไว้โดย Marco
#!/bin/bash
# Exit if /tmp/lock.file exists
[ -f /tmp/lock.file ] && exit
# Create lock file, sleep 1 sec and verify lock
echo $$ > /tmp/lock.file
sleep 1
[ "x$(cat /tmp/lock.file)" == "x"$$ ] || exit
# Do stuff
sleep 60
# Remove lock file
rm /tmp/lock.file
lsof $0
ไม่เลวเลยใช่ไหม
$$
ในไฟล์ล็อค จากนั้นsleep
ช่วงเวลาสั้น ๆ และอ่านมันกลับมา หาก PID ยังคงเป็นของคุณแสดงว่าคุณได้รับการล็อคสำเร็จ ไม่จำเป็นต้องมีเครื่องมือเพิ่มเติม
หากคุณต้องการให้แน่ใจว่าสคริปต์ของคุณทำงานเพียงตัวอย่างเดียวให้ดูที่:
ล็อคสคริปต์ของคุณ (เทียบกับการรันแบบขนาน)
มิฉะนั้นคุณสามารถตรวจสอบps
หรือเรียกใช้lsof <full-path-of-your-script>
เนื่องจากฉันจะไม่เรียกเครื่องมือเพิ่มเติม
อาหารเสริม :
ที่จริงฉันคิดว่าจะทำแบบนี้:
for LINE in `lsof -c <your_script> -F p`; do
if [ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running" 1>&2
exit 1;
fi
done
สิ่งนี้ช่วยให้มั่นใจได้ว่าเฉพาะกระบวนการที่มีระดับต่ำสุดเท่านั้นpid
ที่สามารถทำงานต่อได้แม้ว่าคุณจะแยกและทำหลาย ๆ อินสแตนซ์<your_script>
พร้อมกัน
[[(lsof $0 | wc -l) > 2]] && exit
อาจเพียงพอแล้วหรือว่าสิ่งนี้มีแนวโน้มที่จะเกิดสภาพการแข่งขันหรือไม่?
อีกวิธีหนึ่งในการตรวจสอบให้แน่ใจว่าอินสแตนซ์ของสคริปต์ทุบตีทำงาน:
#!/bin/bash
# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running" && exit 1
...
pidof -o %PPID -x $0
รับ PID ของสคริปต์ที่มีอยู่หากมีอยู่แล้วทำงานหรือออกด้วยรหัสข้อผิดพลาด 1 หากไม่มีสคริปต์อื่นกำลังทำงานอยู่
แม้ว่าคุณเคยถามสำหรับการแก้ปัญหาโดยไม่ต้องเครื่องมือเพิ่มเติมนี้เป็นวิธีที่ชื่นชอบของฉันโดยใช้flock
:
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
สิ่งนี้มาจากส่วนตัวอย่างของman flock
ซึ่งอธิบายเพิ่มเติม:
นี่เป็นรหัสสำเร็จรูปที่เป็นประโยชน์สำหรับเชลล์สคริปต์ วางไว้ที่ด้านบนของเชลล์สคริปต์ที่คุณต้องการล็อคและมันจะล็อคตัวเองโดยอัตโนมัติในการรันครั้งแรก หาก env var $ FLOCKER ไม่ได้ถูกตั้งค่าเป็นเชลล์สคริปต์ที่กำลังทำงานอยู่ให้เรียกใช้งานฝูงและคว้าล็อคที่ไม่มีการปิดกั้นแบบเอกสิทธิ์ นอกจากนี้ยังตั้งค่า FLOCKER env var ให้เป็นค่าที่ถูกต้องดังนั้นจึงไม่ทำงานอีกครั้ง
คะแนนที่ต้องพิจารณา:
flock
สคริปต์ตัวอย่างสิ้นสุดลงพร้อมกับข้อผิดพลาดหากไม่พบดูเพิ่มเติมhttps://stackoverflow.com/questions/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at
นี้เป็นรุ่นที่ปรับเปลี่ยนจากคำตอบอันเซลโม่ แนวคิดคือการสร้าง descriptor ไฟล์อ่านอย่างเดียวโดยใช้สคริปต์ทุบตีเองและใช้flock
เพื่อจัดการล็อค
SCRIPT=`realpath $0` # get absolute path to the script itself
exec 6< "$SCRIPT" # open bash script using file descriptor 6
flock -n 6 || { echo "ERROR: script is already running" && exit 1; } # lock file descriptor 6 OR show error message if script is already running
echo "Run your single instance code here"
ข้อแตกต่างที่สำคัญของคำตอบอื่น ๆ คือรหัสนี้ไม่ได้แก้ไขระบบไฟล์ใช้ footprint ที่ต่ำมากและไม่ต้องการการล้างข้อมูลใด ๆ เนื่องจากไฟล์ descriptor ถูกปิดทันทีที่สคริปต์เสร็จสิ้นโดยไม่ขึ้นกับสถานะทางออก ดังนั้นจึงไม่สำคัญว่าสคริปต์จะล้มเหลวหรือไม่สำเร็จ
exec 6< "$SCRIPT"
ดังนั้นคุณควรจะทำ
ฉันใช้ cksum เพื่อตรวจสอบสคริปต์ของฉันอย่างแท้จริงทำงานเช่นเดียวแม้ฉันจะเปลี่ยนเส้นทางและชื่อไฟล์ไฟล์
ฉันไม่ได้ใช้ไฟล์แทร็ป & ล็อกเพราะหากเซิร์ฟเวอร์ของฉันหยุดทำงานฉันต้องลบไฟล์ล็อคด้วยตนเองหลังจากที่เซิร์ฟเวอร์ขึ้น
หมายเหตุ: จำเป็นต้องมี #! / bin / bash ในบรรทัดแรกสำหรับ grep ps
#!/bin/bash
checkinstance(){
nprog=0
mysum=$(cksum $0|awk '{print $1}')
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(cksum /proc/$i/cwd/$cmd|awk '{print $1}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
หรือคุณสามารถ hardcoded cksum ภายในสคริปต์ของคุณเพื่อให้คุณไม่ต้องกังวลอีกครั้งถ้าคุณต้องการที่จะเปลี่ยนแปลงชื่อไฟล์เส้นทางหรือเนื้อหาของสคริปต์ของคุณ
#!/bin/bash
mysum=1174212411
checkinstance(){
nprog=0
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(grep mysum /proc/$i/cwd/$cmd|head -1|awk -F= '{print $2}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
mysum
และfsum
เมื่อคุณไม่ได้พูดถึงเช็คซัมอีกต่อไป
คุณสามารถใช้สิ่งนี้: https://github.com/sayanarijit/pidlock
sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
รหัสของฉันกับคุณ
#!/bin/bash
script_file="$(/bin/readlink -f $0)"
lock_file=${script_file////_}
function executing {
echo "'${script_file}' already executing"
exit 1
}
(
flock -n 9 || executing
sleep 10
) 9> /var/lock/${lock_file}
จากman flock
การปรับปรุงเท่านั้น:
executing
ที่ฉันใส่ที่นี่sleep 10
คุณสามารถใส่สคริปต์หลักทั้งหมด