2012-11-13 34 views
7

bash劇本我想分配內建一個數組變量times的輸出,但我發現沒有更好的辦法比擊:分配的「次」內置輸出到一個變量

tempnam=/tmp/aaa_$$_$RANDOM 
times > ${tempnam} 
mapfile -t times_a < ${tempnam} 

我將輸出寫入臨時文件並將其讀回到times_a數組中,因爲pipeline或$(times)將在子外殼中執行並返回錯誤的值。

沒有臨時文件的任何更好的解決方案?

回答

6

您需要解決的根本問題是如何在不使用臨時文件的情況下同時執行time和變量賦值。幾乎每種方法Bash都提供了將一個東西的輸出管道輸出到另一個東西,或者捕獲一個命令的輸出,其中一方在子外殼中工作。

這裏是你可以不用臨時文件的一種方法,但我會提醒你,這不是很漂亮,這是無法移植到其他炮彈,它至少需要擊4:

coproc co { cat; }; times 1>&${co[1]}; eval "exec ${co[1]}>&-"; mapfile -tu ${co[0]} times_a 

我會爲你解決這個問題:

coproc co { cat; } 

這創建了一個協處理器;這是一個在後臺運行的進程,但您可以通過管道與標準輸入和標準輸出進行通信,這些FD是${co[0]}(標準爲cat)和${co[1]}(標準爲cat)。這些命令在一個子shell中執行,所以我們不能在那裏執行任何一個目標(運行times或讀入一個變量),但我們可以使用cat來簡單地將輸入傳遞給輸出,然後使用該管道在當前shell中與timesmapfile交談。

times >&${co[1]}; 

運行times,其重定向標準輸出到標準的cat命令。

eval "exec ${co[1]}>&-" 

關閉cat命令的輸入結束。如果我們不這樣做,cat將繼續等待輸入,保持其輸出打開,並且mapfile將繼續等待輸入,導致您的shell掛起。 exec,當不傳遞任何命令時,只需將其重定向應用到當前shell;重定向到-關閉FD。我們需要使用eval,因爲Bash似乎遇到了exec ${co[1]}>&-的問題,將FD解釋爲命令而不是重定向的一部分;使用eval允許首先替換該變量,然後執行。

mapfile -tu ${co[0]} times_a 

最後我們實際上從協處理器中讀取標準數據。我們設法在這個shell中運行了timesmapfile命令,並且沒有使用臨時文件,儘管我們在兩個命令之間使用了臨時進程作爲管道。

請注意,這有一個微妙的比賽。如果你逐個執行這些命令,而不是全部作爲一個命令,則最後一個命令失敗;因爲當你關閉cat的標準時,它退出,導致協處理器退出並且FD關閉。看起來,當全部在一行上執行時,mapfile被執行得足夠快,以至於協處理在運行時仍然打開,因此它可以從管道讀取;但我可能會很幸運。我還沒有想出解決這個問題的好方法。

總而言之,只需寫出臨時文件就簡單多了。我會用mktemp生成一個文件名,如果你在一個腳本的時候,添加陷阱,以保證在退出之前,你清理臨時文件:

tempnam=$(mktemp) 
trap "rm '$tempnam'" EXIT 
times > ${tempnam} 
mapfile -t times_a < ${tempnam} 
+1

我希望我能更給予好評這一點,我已經學會了很多讀這個,真的很好的答案! – higuaro

0

一種可能性做同樣的事情會

times > >(other_command) 

這是一個過程替代combided輸出重定向。這種方式times在當前shell中執行,輸出重定向到一個新的子進程。因此,使用mapfile做這件事並沒有多大意義,因爲該命令不會在同一個shell中執行,而且這可能不是您想要的。這種情況有點棘手,因爲你不能調用在子shell中執行的shell內置函數。

1

哇,好問題。

一個改進就是使用mktemp,這樣你就不會依賴隨機性來保持文件的獨特性。

TMPFILE=$(mktemp aaa_XXXXXXXXXX) 
times > "$TMPFILE" 
mapfile -t times_a < ${tempnam} 
rm "$TMPFILE" 

此外,我使用替代mapfile(因爲我沒有mapfile)。

a=0; for var in $(cat "$TMPFILE"); do ((a++)); TIMES_A[$a]=$var; done 

但是,是啊,我不明白你怎麼能沒有文件或命名管道。