方法1:使用锁文件(推荐)
#!/bin/bash
# mutex_run.sh - 使用锁文件实现互斥
LOCKFILE="/tmp/myapp.lock"
# 检查锁文件是否存在
if [ -f "$LOCKFILE" ]; then
# 检查进程是否还在运行
if kill -0 $(cat "$LOCKFILE") 2>/dev/null; then
echo "程序已在运行 (PID: $(cat $LOCKFILE))"
exit 1
else
echo "检测到旧的锁文件,删除中..."
rm -f "$LOCKFILE"
fi
fi
# 创建锁文件并写入当前PID
echo $$ > "$LOCKFILE"
# 确保程序退出时删除锁文件
cleanup() {
rm -f "$LOCKFILE"
exit
}
trap cleanup EXIT
# 在这里放置你的程序代码
echo "程序开始运行 (PID: $$)"
sleep 30 # 模拟程序运行
echo "程序运行结束"
方法2:使用flock(Linux专用)
#!/bin/bash
# flock_mutex.sh - 使用flock实现互斥
LOCKFILE="/tmp/myapp_flock.lock"
# 尝试获取锁
exec 200>"$LOCKFILE"
if ! flock -n 200; then
echo "程序已被其他实例运行"
exit 1
fi
# 程序主体
echo "程序开始运行 (PID: $$)"
sleep 30
echo "程序运行结束"
# 释放锁
flock -u 200
方法3:使用mkdir原子操作
#!/bin/bash
# mkdir_mutex.sh - 使用mkdir的原子性
LOCKDIR="/tmp/myapp_mutex"
# 尝试创建目录(原子操作)
if ! mkdir "$LOCKDIR" 2>/dev/null; then
echo "程序正在运行中"
exit 1
fi
# 确保退出时删除目录
trap 'rm -rf "$LOCKDIR"' EXIT
# 程序主体
echo "程序开始运行 (PID: $$)"
sleep 30
echo "程序运行结束"
方法4:使用系统信号量(Linux)
#!/bin/bash
# sem_mutex.sh - 使用信号量实现互斥
SEM_NAME="myapp_sem"
# 创建信号量
if ! semaphore_ac "$SEM_NAME" 2>/dev/null; then
# 如果没有semaphore工具,使用备用方案
echo "程序已在运行"
exit 1
fi
# 程序主体
echo "程序开始运行"
sleep 30
echo "程序运行结束"
# 释放信号量
semaphore_re "$SEM_NAME"
方法5:使用Python实现(跨平台)
#!/usr/bin/env python3
# mutex.py - Python实现互斥运行
import os
import sys
import fcntl
import time
class Mutex:
def __init__(self, lock_file="/tmp/python_app.lock"):
self.lock_file = lock_file
self.fd = None
def acquire(self):
try:
self.fd = open(self.lock_file, 'w')
fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
return True
except (IOError, OSError):
return False
def release(self):
if self.fd:
fcntl.flock(self.fd, fcntl.LOCK_UN)
self.fd.close()
os.remove(self.lock_file)
def main():
mutex = Mutex()
if not mutex.acquire():
print("程序已在运行")
sys.exit(1)
try:
print(f"程序开始运行 (PID: {os.getpid()})")
time.sleep(30) # 模拟程序运行
print("程序运行结束")
finally:
mutex.release()
if __name__ == "__main__":
main()
方法6:使用Shell脚本封装
#!/bin/bash
# wrapper.sh - 封装任意命令的互斥运行
LOCKFILE="/tmp/command_lock"
TARGET_COMMAND=$@
if [ -z "$TARGET_COMMAND" ]; then
echo "用法: $0 <命令>"
exit 1
fi
# 尝试获取锁
if [ -f "$LOCKFILE" ]; then
if kill -0 $(cat "$LOCKFILE") 2>/dev/null; then
echo "命令 '$TARGET_COMMAND' 已在运行"
exit 1
fi
fi
echo $$ > "$LOCKFILE"
trap 'rm -f "$LOCKFILE"; exit' EXIT INT TERM
# 执行目标命令
$TARGET_COMMAND
使用建议
-
生产环境推荐:使用
flock(方法2)或锁文件(方法1)
-
防止死锁:添加超时机制
# 带超时的锁文件检查 LOCKFILE="/tmp/app.lock" TIMEOUT=5
等待锁释放(最多等待5秒)
for i in $(seq 1 $TIMEOUT); do if [ -f "$LOCKFILE" ]; then if kill -0 $(cat "$LOCKFILE") 2>/dev/null; then if [ $i -eq $TIMEOUT ]; then echo "等待超时,退出" exit 1 fi sleep 1 else rm -f "$LOCKFILE" break fi else break fi done
3. **日志记录**:添加运行日志
```bash
echo "$(date): 程序启动 (PID: $$)" >> /var/log/app_start.log
选择哪种方法取决于你的具体需求:
- 简单场景:锁文件法
- Linux系统:flock或信号量
- 跨平台需求:Python实现
- 原子操作要求:mkdir法