Linux shell腳本單進程執行

簡要

在持續集成(CI)中, 我們的項目使用的是 shell, 某個stages是需要單例執行(因爲要獨佔進程). 因此想到了要使用單例. 等待執行.

第一種方案

代碼

#!/bin/bash
file_name=`basename $0`
echo $file_name
while [ `pgrep -f ${file_name} | wc -l` -gt 2 ]; do
        echo ${file_name} process existed, sleep 5s
        sleep 5
done

echo 開始執行
sleep 8
echo 執行結束

解釋

bashename $0 只取文件名
例如 bash ~/demo/test.sh $0=~/demo/test.sh bashename $0=test.sh

pgrep -f ${file_name}輸出含有file_name的pid
wc -l 計數
理論上 大於1即可, 但是爲什麼要大於2呢, 本身佔一個進程(bash ./test.sh), 內部又有執行外部命令pgrep -f ${file_name} | wc -l 佔一個進程, 共兩個進程, 如果在執行一次bash ./test.sh必定大於2了.

該方法理論上看上去沒什麼問題, 但是當堆積起來時(比如堆積3個時), 必定都陷入了等待. 兩個是沒有問題的看圖.
稍微改了下代碼, 便於查看 count數

#!/bin/bash
file_name=`basename $0`
echo $file_name
count=`pgrep -f ${file_name} | wc -l`
while [ $count -gt 2 ]; do
        echo ${file_name} process existed, sleep 5s count=$count
        sleep 5
        count=`pgrep -f ${file_name} | wc -l`
done

echo 開始執行
sleep 8
echo 執行結束

結果顯示

只有兩個的情況下
在這裏插入圖片描述
三個情況, 就會死循環了…
在這裏插入圖片描述
因此這種方法被捨棄, 主要還有一點就是pgrep -f ${file_name}輸出含有file_name的pid, 注意是含有, 當有執行例如vim test.sh時候, 也會記錄count

第二種方案

使用flock, 先看看 flock如何使用

$ flock -h

Usage:
 flock [options] <file>|<directory> <command> [<argument>...]
 flock [options] <file>|<directory> -c <command>
 flock [options] <file descriptor number>

Manage file locks from shell scripts.

選項:
 -s, --shared             get a shared lock
 -x, --exclusive          get an exclusive lock (default)
 -u, --unlock             remove a lock
 -n, --nonblock           fail rather than wait
 -w, --timeout <secs>     wait for a limited amount of time
 -E, --conflict-exit-code <number>  exit code after conflict or timeout
 -o, --close              close file descriptor before running command
 -c, --command <command>  run a single command string through the shell
     --verbose            increase verbosity

 -h, --help     display this help and exit
 -V, --version  output version information and exit

For more details see flock(1).

-s爲共享鎖,在定向爲某文件的FD上設置共享鎖而未釋放鎖的時間內,其他進程試圖在定向爲此文件的FD上設置獨佔鎖的請求失敗,而其他進程試圖在定向爲此文件的FD上設置共享鎖的請求會成功。
-e爲獨佔或排他鎖,在定向爲某文件的FD上設置獨佔鎖而未釋放鎖的時間內,其他進程試圖在定向爲此文件的FD上設置共享鎖或獨佔鎖都會失敗。只要未設置-s參數,此參數默認被設置。
-u手動解鎖,一般情況不必須,當FD關閉時,系統會自動解鎖,此參數用於腳本命令一部分需要異步執行,一部分可以同步執行的情況。
-n爲非阻塞模式,當試圖設置鎖失敗,採用非阻塞模式,直接返回1,並繼續執行下面語句。
-w設置阻塞超時,當超過設置的秒數,就跳出阻塞,返回值設置爲1,並繼續執行下面語句。
-o必須是使用第一種格式時纔可用,表示當執行command前關閉設置鎖的FD,以使command的子進程不保持鎖。
-c執行其後的comand。

最終使用 flock -n

代碼

#!/bin/bash
file_name=`basename $0`
echo $file_name

readonly LOCKFILE="~/$file_name.pid"
readonly FD=$(ls -l /proc/$$/fd | sed -n '$p' | awk '{print $9}')
readonly LOCKFD=$(( ${FD}+1 ))

eval "exec ${LOCKFD}>${LOCKFILE}"
flag=0
while [ $flag -eq 0 ]; do
        flag=1
        flock -n ${LOCKFD} && echo $$>&${LOCKFD} || flag=0
        echo $flag
        sleep 1
done

echo "開始執行"
sleep 5
echo "z執行結束"

解釋

readonly表示變量只讀
LOCKFILE 正如 在使用apt-get的時候發生錯誤會說 xxx.pid被佔用, 跟那個同樣的道理
FD文件描述符
eval "exec ${LOCKFD}>${LOCKFILE}" 打開文件LOCKFILE並把它關聯到文件描述符LOCKFD
while之後就一個循環, 獲得到鎖, 就不會執行 || flag=0 就會退出循環
flock -n ${LOCKFD}嘗試獲得鎖, 得到鎖, 會把自己的pid$$寫入到文件中.

結果

在這裏插入圖片描述

最後

可能有人會說, 怎麼不需要釋放鎖, 因爲, 程序結束, 會自動釋放釋放文件描述符, 也就自動釋放了鎖, 如果程序中還有別的操作, 建議手動釋放.

可能還有人會說, 我想讓它執行一次就行了, 不需要每個腳本都執行.那麼把代碼改成這樣就行了, ||flag=0改成’exit 1`即可

#!/bin/bash
file_name=`basename $0`
echo $file_name

readonly LOCKFILE="~/$file_name.pid"
readonly FD=$(ls -l /proc/$$/fd | sed -n '$p' | awk '{print $9}')
readonly LOCKFD=$(( ${FD}+1 ))

eval "exec ${LOCKFD}>${LOCKFILE}"
flock -n ${LOCKFD} && echo $$>&${LOCKFD} || exit 1

echo "開始執行"
sleep 5
echo "z執行結束"

其他shell就直接退出了, 不在執行
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章