2012-02-16 67 views
2

是否有一種簡單的方法在我的bash腳本中「倒回」/dev/stdin,該腳本已經從輸入管道讀取了全部或部分內容?在bash腳本中倒回stdin

應用:我寫了一個簡單的MDA,在第1部分,從fetchmail的逐行讀取單個電子郵件,像這樣:

while read -a linA; do 
    echo -e "$[++linenum]:\t${#linA[@]},${linA[*]}" > /dev/null # verbose 
    [ "${linA[0]}" = "Date:" ] && unset linA[0] && mailDate="${linA[*]}" 
    [ "${linA[0]}" = "Subject:" ] && unset linA[0] && mailSubject="${linA[*]}" 
    [ "$mailSubject" = "Courtesy Fill Notification" ] || break # if wrong subject then thank you, we're done with this mail 
done 

,並在處理結束時,我希望保存整個消息寫入一個文件中,以便進行調試,並且管道的寫入器端可以看到它的整個輸出已經被讀取,並且不會返回失敗(因此將郵件保持爲未讀取在郵箱中)。

回答

5

讀管是破壞性的;有沒有辦法尋求管道上:

ESPIPE (29): Illegal seek 

錯誤編號爲MacOS X系統,但名字是傳統的(和POSIX的規定),並給出了其中限制來自指示。

所以,如果你想重新處理輸入shell腳本,您將需要藏匿標準輸入走在一個文件中,以便您可以重新處理:

tmp=${TMPDIR:-/tmp}/xx.$$ # Consider mktemp or something 
trap "rm -f $tmp.1; exit 1" 0 1 2 3 13 15 # Exit, HUP, INT, QUIT, PIPE, TERM 

tee $tmp.1 | 
while read -a linA 
do 
    ... 
done 

...reprocess $tmp.1 here... 

rm -f $tmp.1 
trap 0 
exit $exit_status 

唯一的陷阱看是因爲在父級shell中無法訪問在while循環中設置的變量。如果這是個問題,您可以使用:

tee $tmp.1 | 
{ 
while read -a linA 
do 
    ... 
done 

...reprocess $tmp.1 here... 
} 

大括號爲I/O重定向目的分組語句。由於EOF,tee進程已完成,因此當while read檢測到EOF時文件將完成,因此在循環後訪問$tmp.1是安全的。

有一點需要注意的是,tee將使這對終端輸入無響應。文件不會是一個問題;管道輸入不太可能成爲問題。但是,tee可能會完全緩衝其輸出,而不是行緩衝其輸出,因此,只有輸入了許多行之後,讀取循環纔會看到任何內容。

+0

++退出循環,因爲我喜歡你的bash技巧在前三個代碼行中。無論如何,我的mda.sh腳本總是以非交互方式使用......即使是爲了測試,我也會重定向/將測試文件cat給它。 – Marcos 2012-02-16 15:38:26

+0

出於好奇,你試圖尋找什麼代碼來獲得'ESPIPE'錯誤?我可能會用於非管道情況。 – Marcos 2012-02-16 16:12:19

+1

我實際上運行了一個我編寫/生成的程序,名爲「errno」,它打印出與數字指定的錯誤編號或字符串(我指定的ESPIPE)對應的信息。如果你這樣做,你會得到它:'int p [2],e;管(P); lseek(p [0],-1L,2); e = errno; printf(「%d(%s)\ n」,e,strerror(e));',除了符號名稱比這更難獲取。 (我有一個Perl腳本來生成'errno'的實際代碼。) – 2012-02-16 16:38:50

0

不是,沒有。

您只需在讀取變量時將每行添加到變量中,並根據需要清除該變量。

+0

除了經常從'break' – Marcos 2012-02-16 15:40:15

0

怎麼樣:

tmpfile=$(mktemp) 
while read -a linA; do 
    echo -e "$[++linenum]:\t${#linA[@]},${linA[*]}" > /dev/null # verbose 
    [ "${linA[0]}" = "Date:" ] && unset linA[0] && mailDate="${linA[*]}" 
    echo "${linA}">>$tmpfile 
done 
mv $tmpfile fulltext.txt 

我認爲這是更好的辦法,因爲你讀的消息只是一次

+0

謝謝。因爲我打算將輸入保存到文件中,所以我認爲我將跳過這整個_tmpfilename_事務,並在開始時寫入固定文件名,而不是結束。這需要稍微改變我的循環。所以我想我可能會用'cat/dev/stdin> $ inputfile'跟隨'cat $ inputfile |而讀-a ...' – Marcos 2012-02-16 16:24:04

0

嘗試exec < /dev/stdin可能在Linux下工作。