用 mkdir 確保 shell script 執行的互斥性

有些時候,你不希望一個 script 同時有兩個以上的 instance,這個時候你可能有很多想法,例如利用檢查 pid file 的方法,或者是找一個 lockf(1) 之類的程式幫你處理這些事情。(老實說 FreeBSD 的 lockf(1) 的確蠻方便的,尤其是對於懶惰寫 code 的我來說。)

但是這會有另外一個問題,不是每個 OS 都有提供 lockf(1),有些就只給你 lockf(2)。所以我就得換個方法作,首先,我想到這樣的寫法

if [ -f $lockfile] ; then 
    touch $lockfile 

利用一個 lockfile 來確保互斥性,只不過 -f 與 touch 之間還是有機會產生 race condition。結果在讀了一些文章之後,我發現有人說 mkdir(2) 是 atomic 的(我目前還沒有找到什麼佐證的資料,NFS 就先放一旁吧……),也有人就用 mkdir(1) 來確保同時只會有一個 shell script 的 instance,再搭配 trap 來處理一些 signal,這樣似乎就可以暫時取代 lockf 了……。

最後的版本就像這樣,應該還有很多問題,但先這樣吧。

ToLock()
{
        progName=`/bin/basename $0`
        lockname="/tmp/$progName.lock"

        if /bin/mkdir "$lockname"
        then
                # Remove lock directory when script terminates
                trap '/bin/rmdir "$lockname"' 0 1 2 3 15
                trap "exit 2" 1 2 3 15
        else
                echo >&2 "another $progName is running?"
                exit 1
        fi
}

最後附上幾篇參考資料

聽 128kbps 的台北愛樂

#!/bin/sh

url=`/usr/local/bin/wget -q -O - \
'http://hichannel.hinet.net/player/radio/index.jsp?radio_id=228' \
 | grep setMovieFile | awk 'BEGIN {FS = "\""} \
 {print "http://hichannel.hinet.net/player/radio/"$2}'`

mmsurl=`/usr/local/bin/wget -q -O - "$url" | grep Radio_ | \
head -1 | awk 'BEGIN {FS = "\""} {print $2}'`

echo ${mmsurl}

jnlin 以前寫的改,如果你用 foobar2000 的話也可以搭配 Acropolis 寫的 foo_input_mslive 這個 plug-in 來聆聽。