你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

Shell脚本单例运行

[复制链接]
gaosmile 发布时间:2021-2-7 10:20
假设你的一个脚本已经在运行了,如果避免再次被执行呢?也就是如何实现单例运行?
看起来可行的方法
一个非常简单的思路就是,新的脚本被执行时,先检测当前脚本是否有其他实例正在运行,如果有则直接退出。
#!/usr/bin/env bash
3 d( O- p% o2 y2 H#test.sh 来源:公众号编程珠玑
5 x( [2 ~; r3 N. d! W#获取当前运行的test.sh脚本数$ e# {1 Y/ R% O6 ]2 {# P
runCount=$(ps -ef|grep test.sh | grep -v grep -c)
9 i* ?8 Q, Q1 d- zif [ "${runCount}" -ge 1 ]+ ?# U  T  t( L8 }6 \+ z/ |) [
then
: _2 ~  V2 a& m2 L: k    echo -e "test.sh already running,num{runCount}"
/ e4 J* W1 Y/ e: T/ t4 H0 S) v+ W    exit 1;
! }8 e, D  \1 b9 S( I4 t, cfi
/ h/ X5 |  E6 o3 l5 wwhile true
& J% G1 v; r+ q+ {5 {7 Mdo
& ?) @  ~  J9 {/ |9 \    echo "test.sh run"4 t( l& q) \; v4 F, D; z4 v
    sleep 1
7 R- C7 B  `' N  a- ?done( n, u3 H+ L7 m8 K: T" ?( Y
这里通过ps获取到当前在运行的test.sh脚本数,如果大于1,说明已经有在运行的了。
但是你运行会发现,其程序数量不只是一个。
$ ./test.sh  Q2 d7 @. ]& t$ U' ^' c
test.sh already running,num:2
: e" }7 E3 s+ A
惊不惊喜?为什么为这样呢?原因在于,shell脚本中一个命令执行相当于fork了一个进程执行,这里执行的是查找tesh.sh并grep的程序,另外还有一个就是当前运行的脚本程序,这样的方式自然就会出现每次都有两个了。
$ e# U7 w0 W6 o1 t当然判断条件这里你可以换一下,例如数量大于2,但终归不太好。
文件锁
实际上这种方法你已经在《如何让你的程序同时只有一个在运行》介绍过了,只不过之前是用于编写C/C++程序,而这里是用于shell脚本。
我们来回顾一下,这是一个怎样的过程:
  • 1.运行前检查是否有该锁文件,并且文件中的进程正在运行
  • 2.如果有并且程序正在运行,则已经有实例在运行
  • 3.否则,无实例,创建锁文件,写入进程id
  • 4.退出时,删除锁文件
    + k7 l% W+ V/ U" {% E
解释一下第一条,为什么一定要确定锁文件中的进程正在运行,因为,有些情况下如果运行的时候退出没有删除该文件,则会导致新的实例永远无法运行。
#!/usr/bin/env bash
! \4 d% {2 w" @#来源:公众号编程珠玑  f$ l" P4 s, i, ~  \- A
LOCKFILE=/tmp/test.lock' B% g# B- i2 ^2 c) `
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
# F  w1 G! k- ^* }2 c& D. e9 u+ r1 m    echo " $0 already running"
- W7 X; ?) \! v0 M    exit
" L; k: {  u8 Efi
8 v+ L1 D; @1 i, 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
# 做你需要的事情
2 R4 I, `" E& L! ?% \sleep 1000  E1 d9 X" ]. p! b" q+ ]

7 R" ^  z( B5 l/ G+ `# 删除锁文件
) I, T6 I! e# N: ?3 nrm -f ${LOCKFILE}+ P4 {$ M" j, m- j$ X$ N
我们试着运行其中一个,然后另外一个窗口尝试运行:
$ ./test.sh: ~/ M: Y$ x# w- y2 i, P3 @" K
./test.sh already running
5 F% s! ^9 b0 A
由于已经有实例在运行,发现新的程序无法运行了。而等旧的脚本运行完之后,新的就可以运行了。
实际上这里面有几个点非常巧妙:
  • kill -0 `cat \${LOCKFILE}`  这里用于检测该进程是否存在,避免进程不在了,但是锁文件还在,导致后面的脚本无法运行。
  • trap "rm -f \${LOCKFILE}; exit" INT TERM EXIT 用于确保脚本退出时,锁文件会被删除。
  • rm -f {LOCKFILE}  脚本最后需要删除锁文件

    . x2 {% e! d9 f. a
flock
说到锁文件,这里就不得不提flock命令了。没有前面的一些巧妙处理,我们很多时候会很难删除原先创建的锁文件,比如:
  • 脚本被意外中断,没来得及执行删除
  • 多个脚本产生竞争,导致判断异常,比如前面有一个脚本运行,判断没有锁文件,下一步准备创建,但是另外一个脚本又先创建了,就会导致异常了。

    * k4 {+ S% P# {
因此我们可以考虑使用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"
* K7 ^! F( O! i+ y0 k7 X7 N- Lflock -n 99
& ~5 R$ r; f* c; Jif [ "$?" != 0 ]; then4 d/ N) p% |, ]* p3 H  Y: O
    echo "$0 already running"$ k7 g) {0 l' d
    exit 1
2 t% `: y3 K: L- Tfi/ S9 C( K" n6 N" y3 w% a
#脚本要做的其他事情
9 l3 E+ F0 t- k3 C9 F' e* dsleep 1024
. H7 k9 r% ?4 S# U4 d6 c! W4 W  U
解释一下:
  • exec 99>"$LOCK_FILE" 表示创建文件描述符99,指向锁文件,为何是99?110其实也是可以的,只是为了和当前脚本可能打开的文件描述符冲突(例如和0,1,2冲突)。
  • flock -n 99  尝试对该文件描述符加锁,由操作系统保证原子性
  • 一旦flock失败了,我们这里可以退出
  • 而即使锁定了,脚本退出后,也会自动释放
    7 }' b' C0 Y4 r4 D- b
因此这里避免了锁没有释放的情况。
另一种做法
查看flock的man手册,我们发现它还有一个例子是这么做的:
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en  "$0"  "$0"  "$@" || :
6 a. m  u7 u. T
在脚本开头加上上面这么一行就可以了。例如:
#!/usr/bin/env bash/ d8 X: Q4 A' J% M. E
#来源:公众号编程珠玑
% f3 ~1 x; `) H) L" P' Z8 \: s[ "${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
解释一下:
  • 如果${FLOCKER}环境变量没有设置,则尝试将脚本本身加锁,如果加锁成功,则运行当前脚本,(并且带上原有的参数),否则的话静默退出。

    6 I2 J0 f) g6 z* v5 u# R0 ^' M
总结
单例运行本身思路是很简单的,就是探测当前是否有实例在运行,如果有,则退出,但是这里如何判断,却并不是那么容易。
最后,总结一下本文出现的一些该掌握的信息
  • $0  脚本名称
  • $@  脚本参数
  • $$ 当前脚本进程id
  • $? 上一条命令执行结果
  • 描述符0 标准输入
  • 描述符1 标准输出
  • 描述符2 标准错误
  • > 重定向

    9 h# a( n! y- q/ t$ q# i6 |

' H/ B+ Y  z2 d, b. K9 d9 c6 z( W8 p
赞 收藏 评论0 发布时间:2021-2-7 10:20

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版