在 shell 腳本中超時
我有一個從標準輸入讀取的shell 腳本。在極少數情況下,沒有人準備好提供輸入,腳本必須超時。如果超時,腳本必須執行一些清理程式碼。最好的方法是什麼?
這個腳本必須非常便攜,包括 20 世紀沒有 C 編譯器的 unix 系統和執行 busybox 的嵌入式設備,所以 Perl、bash、任何編譯語言,甚至是完整的 POSIX.2 都不能依賴。特別是
$PPID
,read -t
和完全符合 POSIX 的陷阱不可用。寫入臨時文件也被排除在外;即使所有文件系統都以只讀方式掛載,該腳本也可能會執行。只是為了讓事情變得更加困難,我還希望腳本在不超時時相當**快。**特別是我也在 Windows 中使用腳本(主要是 Cygwin),其中 fork 和 exec 的使用率特別低,所以我想盡量減少它們的使用。
簡而言之,我有
trap cleanup 1 2 3 15 foo=`cat`
我想添加一個超時。我無法
cat
用read
內置替換。在超時的情況下,我想執行該cleanup
功能。背景:該腳本通過列印一些8位字元並比較前後游標位置來猜測終端的編碼。腳本的開頭測試stdout 是否連接到受支持的終端,但有時環境是謊言(例如,即使它被呼叫也設置
plink
)TERM=xterm``TERM=dumb
。腳本的相關部分如下所示:text='Éé' # UTF-8; shows up as Ãé on a latin1 terminal csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n" # ␛ is an escape character stty_save=`stty -g` cleanup () { stty "$stty_save"; } trap 'cleanup; exit 120' 0 1 2 3 15 # cleanup code stty eol 0 eof n -echo # Input will end with `0n` # echo-n is a function that outputs its argument without a newline echo-n "$dsr_cpr$dsr_ok" # Ask the terminal to report the cursor position initial_report=`tr -dc \;0123456789` # Expect ␛[42;10R␛[0n for y=42,x=10 echo-n "$text$dsr_cpr$dsr_ok" final_report=`tr -dc \;0123456789` cleanup # Compute and return initial_x - final_x
如何修改腳本,以便如果
tr
2 秒後沒有讀取任何輸入,它會被殺死並且腳本執行cleanup
函式?
那這個呢:
foo=`{ { cat 1>&3; kill 0; } | { sleep 2; kill 0; } } 3>&1`
那就是:執行輸出生成命令,並
sleep
在同一個程序組中,一個只為他們服務的程序組。無論哪個命令首先返回都會殺死整個程序組。有沒有人想知道:是的,管道沒有使用;使用重定向繞過它。它的唯一目的是讓 shell 在同一個程序組中執行這兩個程序。
正如 Gilles 在他的評論中指出的那樣,這在 shell 腳本中不起作用,因為腳本程序將與兩個子程序一起被終止。
強制命令在單獨的程序組中執行的一種方法是啟動一個新的互動式 shell:
#!/bin/sh foo=`sh -ic '{ cat 1>&3; kill 0; } | { sleep 2; kill 0; }' 3>&1 2>/dev/null` [ -n "$foo" ] && echo got: "$foo" || echo timeouted
但是這可能有一些警告(例如,當 stdin 不是 tty 時?)。當互動式 shell 被殺死時,stderr 重定向可以消除“終止”消息。
用
zsh
和bash
測試dash
。但是老歌呢?B98建議進行以下更改,在 Mac OS X 上使用 GNU bash 3.2.57 或在 Linux 上使用破折號:
foo=`sh -ic 'exec 3>&1 2>/dev/null; { cat 1>&3; kill 0; } | { sleep 2; kill 0; }'`
– 1.看起來不標準的
除外。
setsid