Bourne shell 是否有等效的「read -n」指令?

Bourne shell 是否有等效的「read -n」指令?

有沒有辦法讓我的腳本等待用戶按任意鍵然後繼續,而無需他們按Enter?我想讓它在 Bourne shell ( sh) 中工作,而不是在 Bash 中。

答案1

初步說明

如今sh不一定是遺留的 Bourne shell。現在sh是一個shell,支持至少POSIX 所需的功能(假設實現正確)。 POSIX 指定sh公用事業外殼命令語言

我假設您希望您的程式碼在 POSIX shell 中工作並且通常是可移植的。


問題

dd ibs=1 count=…是讀取確切位元組數的 POSIX 方法。它似乎是唯一可以可靠地完成這項工作的便攜式命令列實用程式。但dd讀取字節,而read -n在 Bash 中讀取人物POSIX 允許在以下語言環境中使用多位元組字符POSIX。即使您使用 執行整個腳本LC_ALL=POSIX,終端機(終端機模擬器)仍可能在按一下某個按鍵時產生多位元組序列。這樣的序列可能不是多位元組字元;它可能是一個轉義序列(例如對於F1,另見這個答案)。

僅讀取一個位元組後,其餘位元組將保留,並且稍後嘗試從終端讀取的任何內容都會讀取該位元組(它可能是腳本的一部分或運行腳本的互動式shell 的一部分)。

此外,如果你單獨跑步dd ibs=1 count=1,那麼你很可能會發現什麼都沒有達到,dd直到你超過{MAX_INPUT}或者{MAX_CANON},或按EnterCtrl+D(如果count超過1,如果您想提供更少的字節,那麼您可能需要按Ctrl+D 兩次)。

為了解決這些問題你需要非規範模式。使用stty -icanon。一般來說,從開始stty raw看起來是個好主意。


程式碼

以下範例是概念證明。因為它操作終端的線路設定並從終端讀取一定不將其貼到互動式 shell 中,這不起作用。將其貼到文件中並運行該文件。

#!/bin/sh

echo "Press any key to continue."

saveterm="$(stty -g)"                     # save terminal state
stty raw
stty -echo -icanon min 1 time 0           # prepare to read one byte
dd ibs=1 count=1 >/dev/null 2>/dev/null   # read one byte
stty -icanon min 0 time 0                 # prepare to read lefotvers
while read none; do :; done               # read leftovers
stty "$saveterm"                          # restore terminal state

筆記

  • 該腳本不會檢查其標準輸入是否為終端,但一般來說它應該([ -t 0 ])。

  • 在正常的設定中,單獨按下修飾鍵(例如Shift)時,不會向從終端讀取的內容發送任何輸入;因此我們的程式碼不會將此類密鑰註冊為「任意密鑰」。

  • 「讀剩菜」技巧取自這個答案

  • 感謝stty raw Ctrl+CCtrl+Z也可以是“任意鍵”,而不是分別發送SIGINTSIGSTOP。儘管如此,解釋腳本的 shell 可能會從其他地方收到訊號,因此通常它可能會在到達 之前退出stty "$saveterm";因此可能會導致終端處於不適合互動使用的狀態。無論如何,您可能希望捕獲相關訊號並恢復終端的初始狀態。

  • 這是真的應該用雙引號引用變數一般來說。這裡$saveterm故意不引用,因為stty -g產生未指定的輸出。的實作stty允許產生帶有空格的輸出,然後期望 shell 將其分割並傳遞多個參數。指定的是一個約束,即 的輸出stty -g在不加引號時必須是安全的,它不能在 shell 中觸發單字擴展。為了方便移植,不加引號$saveterm比較好。

    編輯:這是 POSIX 規範中的一個缺陷。上面的程式碼已被修復。

  • 要知道讀取了哪個字節,您需要將 的輸出儲存dd到稍後將檢查的常規檔案 ( >some_file) 或變數 ( variable="$(dd …)")。但:

    • 終端能夠產生空字節(通常使用Ctrl+ @),但大多數(全部?)實作sh不能將空字節儲存在變數中。儲存在文件中是可以的,只要您可以檢查/操作其內容而不將其讀入變數即可。
    • dd上面的程式碼只會給你一個字節,它可能不足以告訴你字元或金鑰。 OTOH 單一位元組應該足以區分例如Q與其他任何東西,因此Press Q to quit or any other key to continue.P - proceed; B - back; Q - quit; H - help似乎可以在不分析更多位元組的情況下實現。

更多位元組怎麼辦?

上面的內容對於「按任意鍵」應該沒問題。真正的等價物read -n應該一次讀取一個位元組並對序列進行解碼,直到獲得所需的數量人物。我不會嘗試在這裡構建它。

相關內容