2012-03-29 211 views
15

我目前正試圖讓腳本將其他已啓動命令的輸出正確寫入日誌文件。該腳本會使用echo將它自己的消息寫入日誌文件,並且有一種方法可以將流水線從其他程序中抽取出來。當寫單行時是回聲原子

主要的問題是,產生輸出的程序是在後臺啓動的,所以執行讀操作的函數可能會一致地寫入日誌文件。這可能是一個問題嗎?回聲總是隻寫一行,所以不應該很難確保原子性。然而,我看了谷歌,我發現沒有辦法確保它實際上是原子。

這是當前腳本:

LOG_FILE=/path/to/logfile 

write_log() { 
    echo "$(date +%Y%m%d%H%M%S);$1" >> ${LOG_FILE} 
} 

write_output() { 
    while read data; do 
    write_log "Message from SUB process: [ $data ]" 
    done 
} 

write_log "Script started" 
# do some stuff 
call_complicated_program 2>&1 | write_output & 
SUB_PID=$! 
#do some more stuff 
write_log "Script exiting" 
wait $SUB_PID 

正如你所看到的,該腳本可以寫上都它自己以及因爲重定向輸出。這可能會導致文件havok?

+0

我不認爲bash是這份工作的正確工具。我會推薦一些更強大的功能(perl,python,ruby ..) – Daenyth 2012-03-29 14:28:26

+4

順便說一下,如果你有很多數據進入這個日誌,那麼你可能會發現持續打開和關閉日誌文件是一個壞主意。你可以使用'exec 3 >> $ {LOG_FILE}'永久地打開一個文件,然後隨時用'echo whatever>&3'來寫入它。您可以使用'exec 3>& - '關閉文件,但腳本退出時會發生這種情況。唯一的問題是您必須爲每個打開的文件手動選擇一個數字,並且已經採用了0,1和2。 – ams 2012-03-29 14:44:38

回答

26

echo只是一個簡單的包裝write(這是一個簡化;見下面的血淋淋的細節編輯),所以要確定回聲是否是原子,它是有用的查找寫。從single UNIX specification

原子/非原子:如果寫入一個操作總量不與來自任何其他處理數據交織的寫入是原子的。當有多個寫入者將數據發送給單個閱讀器時,這非常有用。應用程序需要知道有多大的寫請求可以被期望以原子方式執行。這個最大值被稱爲{PIPE_BUF}。 IEEE Std 1003.1-2001的這一卷沒有說明是否超過{PIPE_BUF}字節的寫入請求是原子性的,但要求{PIPE_BUF}或更少字節的寫入是原子性的。

您可以使用簡單的C程序在您的系統上檢查PIPE_BUF。如果你只是打印一行輸出,那不是很可笑,它應該是原子的。

下面是一個簡單的程序來檢查PIPE_BUF值:

#include <limits.h> 
#include <stdio.h> 

int main(void) { 
    printf("%d\n", PIPE_BUF); 

    return 0; 
} 

在Mac OS X,這給了我512(該minimum allowed valuePIPE_BUF)。在Linux上,我得到了4096個。因此,如果你的線路很長,請確保在有問題的系統上檢查它。

編輯添加:我決定在Bash檢查the implementation of echo,以確認它將以原子方式打印。事實證明,echo使用putcharprintf,具體取決於您是否使用-e選項。這些是緩存的stdio操作,這意味着它們填滿一個緩衝區,並且只有在到達換行符(行緩衝模式),緩衝區被填充(以塊緩衝模式)或者顯式清空輸出爲fflush。默認情況下,如果一個流是一個交互式終端,它將處於行緩衝模式,如果它是任何其他文件,則會阻塞緩衝模式。 Bash從不設置緩衝類型,因此對於您的日誌文件,它應該默認爲阻止緩衝模式。然後在end of the echo builtin,Bash calls fflush刷新輸出流。因此,輸出將始終在echo的末尾刷新,但如果不適合緩衝區,可能會提前刷新輸出。

所用緩衝區的大小可能是BUFSIZ,雖然它可能不同;如果使用setbuf明確地設置緩衝區,則默認大小爲BUFSIZ,但沒有可移植的方式來確定實際的緩衝區大小。 BUFSIZ也沒有可移植的指導原則,但是當我在Mac OS X和Linux上測試它時,它的尺寸是PIPE_BUF的兩倍。

這是什麼意思?由於echo的輸出全部被緩衝,所以在填充緩衝區或調用fflush之前,它實際上不會調用write。在這一點上,應該寫出輸出結果,並應用上面提到的原子性保證。如果stdout緩衝區大小大於PIPE_BUF,那麼PIPE_BUF將是可以寫出的最小原子單位。如果PIPE_BUF大於stdout緩衝區大小,則當緩衝區填滿時,流將寫入緩衝區。

因此,echo只能保證自動寫入比PIPE_BUF中較小的一個更短的序列,並且stdout緩衝區的大小很可能是BUFSIZ。在大多數系統上,BUFSIZ大於PIPE_BUF

tl; drecho將自動輸出線,只要這些線足夠短。在現代系統中,你可能安全到512字節,但不可能確定可移植的限制。

+4

下面是普通Unix系統上觀察到的「PIPE_BUF」值的表格:http://ar.to/notes/posix#pipe-buf – 2014-10-08 00:14:24

0

沒有非自願的文件鎖定,但>>操作符是安全的,>操作符是不安全的。所以你的做法是安全的。