|
假设你的一个脚本已经在运行了,如果避免再次被执行呢?也就是如何实现单例运行? 看起来可行的方法一个非常简单的思路就是,新的脚本被执行时,先检测当前脚本是否有其他实例正在运行,如果有则直接退出。 #!/usr/bin/env bash#test.sh 来源:公众号编程珠玑 #获取当前运行的test.sh脚本数$ e# {1 Y/ R% O6 ]2 {# P runCount=$(ps -ef|grep test.sh | grep -v grep -c) if [ "${runCount}" -ge 1 ]+ ?# U T t( L8 }6 \+ z/ |) [ then echo -e "test.sh already running,num {runCount}"exit 1; fi while true do echo "test.sh run"4 t( l& q) \; v4 F, D; z4 v sleep 1 done( n, u3 H+ L7 m8 K: T" ?( Y 这里通过ps获取到当前在运行的test.sh脚本数,如果大于1,说明已经有在运行的了。 但是你运行会发现,其程序数量不只是一个。 $ ./test.sh Q2 d7 @. ]& t$ U' ^' ctest.sh already running,num:2 惊不惊喜?为什么为这样呢?原因在于,shell脚本中一个命令执行相当于fork了一个进程执行,这里执行的是查找tesh.sh并grep的程序,另外还有一个就是当前运行的脚本程序,这样的方式自然就会出现每次都有两个了。 文件锁当然判断条件这里你可以换一下,例如数量大于2,但终归不太好。 实际上这种方法你已经在《如何让你的程序同时只有一个在运行》介绍过了,只不过之前是用于编写C/C++程序,而这里是用于shell脚本。 我们来回顾一下,这是一个怎样的过程:
解释一下第一条,为什么一定要确定锁文件中的进程正在运行,因为,有些情况下如果运行的时候退出没有删除该文件,则会导致新的实例永远无法运行。 #!/usr/bin/env bash#来源:公众号编程珠玑 f$ l" P4 s, i, ~ \- A LOCKFILE=/tmp/test.lock' B% g# B- i2 ^2 c) ` if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then echo " $0 already running" exit fi , s4 X0 |4 d$ ?8 M1 h+ P- E # 确保退出时,锁文件被删除0 q& q5 b" @. n0 \2 P" V; [. V# Y$ c trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT$ o# a2 X1 j: b$ z" y #将当前程序进程id写入锁文件. r' o3 x9 \8 \7 H+ b- Z echo $$ > ${LOCKFILE}( m3 U" w7 r7 r. B# n3 A " ^* Z, b% k, l1 x. g # 做你需要的事情 sleep 1000 E1 d9 X" ]. p! b" q+ ] # 删除锁文件 rm -f ${LOCKFILE}+ P4 {$ M" j, m- j$ X$ N 我们试着运行其中一个,然后另外一个窗口尝试运行: $ ./test.sh: ~/ M: Y$ x# w- y2 i, P3 @" K./test.sh already running 由于已经有实例在运行,发现新的程序无法运行了。而等旧的脚本运行完之后,新的就可以运行了。 实际上这里面有几个点非常巧妙:
说到锁文件,这里就不得不提flock命令了。没有前面的一些巧妙处理,我们很多时候会很难删除原先创建的锁文件,比如:
因此我们可以考虑使用flock: #!/usr/bin/env bash! d) I2 S2 U! [1 J#来源:公众号编程珠玑 q- \7 E: q3 p8 R LOCK_FILE=/tmp/test.lock% b7 |' C5 i/ t3 n- ~ exec 99>"$LOCK_FILE" flock -n 99 if [ "$?" != 0 ]; then4 d/ N) p% |, ]* p3 H Y: O echo "$0 already running"$ k7 g) {0 l' d exit 1 fi/ S9 C( K" n6 N" y3 w% a #脚本要做的其他事情 sleep 1024 解释一下:
因此这里避免了锁没有释放的情况。 另一种做法查看flock的man手册,我们发现它还有一个例子是这么做的: [ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :在脚本开头加上上面这么一行就可以了。例如: #!/usr/bin/env bash/ d8 X: Q4 A' J% M. E#来源:公众号编程珠玑 [ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :4 m {+ V9 h& f6 Q2 p) K6 X #脚本要做的其他事情( X V9 e" P- K w- q3 |, E sleep 1024! M7 }. _6 a9 X1 m 解释一下:
单例运行本身思路是很简单的,就是探测当前是否有实例在运行,如果有,则退出,但是这里如何判断,却并不是那么容易。 最后,总结一下本文出现的一些该掌握的信息
|
微信公众号
手机版