flock 是檔案鎖命令,它可以保證linux系統上程序之間安全的訪問臨界資源,在shell指令碼中,可以用來控制邏輯的互斥性
現有指令碼 a.sh, 內容如下
#!/bin/bash
echo "[`date +'%y-%m-%d %h:%m:%s'`] begin pid:$$..."
sleep 10
echo "[`date +'%y-%m-%d %h:%m:%s'`] end pid:$$..."
在終端(記為終端1)中執行flock -xn ./f.lock -c ./a.sh命令,結果如下
[tt@ecs-centos-7 lock_test]$ flock -xn ./f.lock -c ./a.sh
[2020-12-10 10:10:45] begin pid:5359...
[2020-12-10 10:10:55] end pid:5359...
在上述命令執行期間,開啟另乙個終端(記為終端2),執行同樣的命令,結果如下
[tt@ecs-centos-7 lock_test]$ flock -xn ./f.lock -c ./a.sh
[tt@ecs-centos-7 lock_test]$
上面的命令 flock -xn ./f.lock -c ./a.sh 中
終端1 中執行 flock -xn ./f.lock -c ./a.sh 命令,對 f.lock 檔案加鎖,同時執行 ./a.sh 命令,執行過程會持續10秒左右( sleep 10 語句 )
由於終端2 中 flock -xn ./f.lock -c ./a.sh 命令是在 終端1 命令執行期間執行的,此時終端1 還未釋放 f.lock檔案鎖,再加上 -n選項是非阻塞的,所以終端2程式設計客棧 不會阻塞等待 f.lock 檔案鎖,而是立即返回
終端2 如果執行 flock -x ./f.lock -c ./a.sh 命令,會一直阻塞等待,直到 終端1 釋放 f.lock 檔案鎖,它才會獲取到 f.lock 檔案鎖並開始執 ./a.sh 命令
例項1 中每次都需要執行 flock -xn 檔案鎖 -c ./a.sh 命令,而且每個不能重複執行的指令碼都要分配乙個檔案鎖,還得保證不同的指令碼得使用不同名字的檔案鎖
有沒有辦法做到只要執行 ./a.sh 命令就可以實現 例項1 中的功能呢?
答案:有的
我們把 a.sh 稍微修改下,修改之後的內容如下
#!/bin/bash
echo "[`date +'%y-%m-%d %h:%m:%s'`] 1111 pid:$$...my_lock:$"
[ "$" != "$0" ] && exec env my_lock="$0" flock -xn "$0" "$0" "$@"
echo "[`date +'%y-%m-%d %h:%m:%s'`] begin pid:$$...my_lock:$"
sleep 10
echo "[`date +'%y-%m-%d %h:%m:%s'`] end pid:$$..."
終端1 執行 ./a.sh 命令,輸出如下
[tt@ecs-centos-7 lock_test]$ ./a.sh
[2020-12-10 14:11:35] 1111 pid:5944...my_lock:
[2020-12-10 14:11:35] 1111 pid:5946...my_lock:./a.sh
[2020-12-10 14:11:35] begin pid:5946...my_lock:./a.sh
[2020-12-10 14:11:45] end pid:5946...
在終端1 命令執行期間,終端2 執行 ./a.sh 命令,輸出如下
[tt@ecs-centos-7 lock_test]$ ./a.sh
[2020-12-10 14:11:44] 1111 pid:5976...my_lock:
[2020-12-10 14:11:44]
新的 a.sh 指令碼相比原來新增了第 4、6 兩行
第 4 行是日誌列印
第 6 行說明
$0 是指令碼名字,這裡的值是 ./a.sh
$@ 是傳入 a.sh 指令碼的所有引數
exec 會在當前程序執行它後面緊接著的命令,當前指令碼程序原來還未執行完的命令不會執行了
[ "$" != "$0" ]是判斷 my_lock 環境變數是否和指令碼名字( a.sh )
相同如果程式設計客棧不同,就執行env my_lock="$0"命令 和flock -xn "$0" "$0" "$@"命令
env my_lock="$0" 設定環境變數 my_lock 的值為指令碼名字
flock -xn "$0" "$0" "$@"其實就是flock -xn ./a.sh ./a.sh,它使用當前指令碼名字作為檔案鎖
例項2 中,執行 ./a.sh 命令之後,當執行到第 6 行時,my_lock 變數是空值,所以[ "$" !=www.cppcns.com "$0" ]的結果為 true
exec 命令會忽略掉後面未執行的命令,也即在當前shell程序中 第 6 行之後的命令都不會執行了
緊接著,exec env my_lock="$0" flock -xn "$0" "$0" "$@"命令, 把 my_lock 變數的值設定為當前指令碼名字 ./a.sh ,同時執行 flock -xn "$0" "$0" "$@" 命令,此命令會在乙個新的子shell中執行 ./a.sh ,所以指令碼後續的輸出中列印的程序id和開始時不一樣
同時,由於在 flock -xn "$0" "$0" "$@" 之前執行過 env my_lock="$0",my_lock 變數的值被設定為了 ./a.sh, 所以 flock -xn "$0" "$0" "$@" 命令重新執行 ./a.sh 命令時,
指令碼第 6 行的 [ "$" != "$0" ] 的結果為 false, 第 6 行 exec 後面的命令不會執行,指令碼接著從第 7 行一直執行到最後, 結果輸出 8 和 12 行的日誌也說明指令碼執行完了
例項1 和 例項2 提供了兩種解決 指令碼重複執行的 方式,主要都是利用 flock 命令設定檔案鎖來實現的,例項2 的方式更簡單,只需要在指令碼開頭加上[ "$" != "$0" ] && exec env my_lock="$0" flock -xn "$0" "$0" "$@"語句,呼叫指令碼的命令保持不變
如何解決shell執行派生子shell問題
筆者今天寫了乙個shell script,希望簡化登入 伺服器的問題,可是script寫好之後,卻發現沒有按照預想的情況下得到環境變數。加入如下配置 按照平時的執行方法,我們通常是 等同於以下執行效果 等同於以下執行效果 然後你檢查環境變數,結果發現輸出的都是空值,然後聯絡了一位開源界的前輩 我也不...
如何解決「重複定義」
標頭檔案中一般只包含宣告,不包含變數的定義,如果沒辦法必須在標頭檔案中包含定義的話,多次引用該標頭檔案時,常遇到函式或者變數被重複定義的錯誤,比喻file1.h中定義了int a file2.h中也定義了 int a 此時在file.c中既包含file1.h也包含file2.h,在預編譯是,file...
防止shell指令碼重複執行
利用鎖機制,讓乙個特定的shell指令碼,每次只能執行乙個例項。具體來說,獲得鎖的指令碼例項,能夠繼續往下執行臨界區 沒有獲得鎖的例項,則只能等待。例如,要求指令碼只能順序訪問某個資源,例如磁碟檔案等,就可以參考下面的實現。bin bash file locking using bash.ver 0...