現象:`ps x | grep "import.sh" | grep -c "import.sh"` 計數恆為 2,導致誤判腳本已在運行。
管道執行時,Shell 會為每個命令創建子進程。`grep "import.sh"` 進程本身的命令行包含 `"import.sh"` 字符串,**會匹配到自己**,導致計數 +1。
# 進程列表示例
PID CMD
1234 /bin/bash /path/import.sh
5678 grep import.sh ← 這行也包含 "import.sh",被計數
count=$(ps x | grep "import.sh" | grep -v "grep" | grep -c "import.sh")
# 或更簡潔:
count=$(pgrep -f "import.sh" | wc -l) # pgrep 默認不匹配自身
# 或使用 flock(推薦,原子級鎖):
exec 200>/var/lock/import.lock
flock -n 200 || { echo "腳本已在運行"; exit 1; }
| 方案 | 優點 | 缺點 |
|---|---|---|
| `grep -v grep` | 兼容性好,無額外依賴 | 競態條件:檢查到執行間隙可被繞過 |
| `pgrep -f` | 語義清晰,自動排除自身 | 部分嵌入式系統無 pgrep |
| `flock` 文件鎖 | **原子操作**,無競態,釋放自動 | 需支援 flock 的文件系統(ext4/xfs/NFSv4+) |
| `mkdir` 鎖目錄 | POSIX 原子操作,極高兼容性 | 需手動清理陰殘留目錄 |
#!/bin/bash
LOCK_FILE="/var/lock/$(basename $0).lock"
exec 200>"$LOCK_FILE"
flock -n 200 || { echo "[$(date)] $0 已在運行,退出"; exit 1; }
# 你的業務邏輯
echo "[$(date)] 開始執行..."
sleep 10
echo "[$(date)] 執行結束"
# 腳本結束自動釋放鎖(FD 關閉)
核心原則:**用文件系統鎖替代進程掃描**,解決「檢查-執行」非原子性導致的並發缺陷。