簡體   English   中英

Bash腳本:暫停倒計時並按ENTER(或任何)恢復它

[英]Bash Script: Pause a countdown and resume it by pressing ENTER (or any)

我有一個運行bash腳本的任務調度程序。 該任務首先打開一個GIT Bash終端,顯示一條打開的消息(“腳本即將在60秒內啟動。”)並在倒計時結束時運行一個腳本。

現在,我想改善用戶體驗,允許他/她停止/恢復倒計時或(無需任何干預)讓腳本自動運行。 那么,這是程序流程:

  1. GIT Bash終端打開后,允許用戶在按ENTER或任何其他鍵的時間范圍內暫停腳本;
  2. 如果用戶未采取任何操作,倒計時將繼續,腳本將在時間范圍結束時運行;
  3. 如果用戶按下了ENTER(或任何其他),則再次按下ENTER(或任何其他鍵),他/她將恢復倒計時並立即運行。

我已經嘗試使用read -p但它對我不好:我不希望用戶操作觸發某些東西而是停止/暫停倒計時(然后恢復它)。

更新記錄:

  • 在答案的第一部分實現了Pausable Countdown (打印很多行)
  • 在第二部分中實現了一個不那么詳細的Pausable Timeout (在按鍵時打印一條靜態行+附加消息)
  • 在第三個代碼片段中一個更復雜的Pausable倒計時不斷更新同一行

可傾倒的倒計時

結合這里類似問題的一些提示和一些關於如何讀取單個字符的外部資源(例如,這里,否則在互聯網上的任何地方) ,並添加一個額外的恢復循環,這就是我提出的:

#!/bin/bash

# Starts a pausable/resumable countdown.
# 
# Starts the countdown that runs for the
# specified number of seconds. The 
# countdown can be paused and resumed by pressing the
# spacebar. 
#
# The countdown can be sped up by holding down any button
# that is no the space bar.
#
# Expects the number of seconds as single
# argument.
#
# @param $1 number of seconds for the countdown
function resumableCountdown() {
  local totalSeconds=$1
  while (( $totalSeconds > 0 ))
  do
    IFS= read -n1 -t 1 -p "Countdown $totalSeconds seconds (press <Space> to pause)" userKey
    echo ""
    if [ "$userKey" == " " ]
    then
      userKey=not_space
      while [ "$userKey" != " " ]
      do
        IFS= read -n1 -p "Paused, $totalSeconds seconds left (press <Space> to resume)" userKey
    echo ""
      done
    elif  [ -n "$userKey" ]
    then
      echo "You pressed '$userKey', press <Space> to pause!"
    fi
    totalSeconds=$((totalSeconds - 1))
  done
}

# little test
resumableCountdown 60

這可以保存並作為獨立腳本運行。 該功能可以在其他地方重復使用。 它暫停/恢復與SPACE ,因為這對我來說似乎更直觀,因為它是如何工作的,例如在嵌入瀏覽器的視頻播放器中。

倒數也可以通過按空格鍵以外的鍵來加速(這是一個功能)。


發出警告消息並等待可暫停的超時

以下變體實現了可暫停的超時 ,除了用戶通過按空格鍵暫停或恢復(內部)倒計時之外, 它只打印初始警告消息:

# Prints a warning and then waits for a
# timeout. The timeout is pausable.
#
# If the user presses the spacebar, the 
# internal countdown for the timeout is 
# paused. It can be resumed by pressing
# spacebar once again.
#
# @param $1 timeout in seconds
# @param $2 warning message
warningWithPausableTimeout() {
  local remainingSeconds="$1"
  local warningMessage="$2"
  echo -n "$warningMessage $remainingSeconds seconds (Press <SPACE> to pause)"
  while (( "$remainingSeconds" > 0 ))
  do
    readStartSeconds="$SECONDS"
    pressedKey=""
    IFS= read -n1 -t "$remainingSeconds" pressedKey
    nowSeconds="$SECONDS"
    readSeconds=$(( nowSeconds - readStartSeconds ))
    remainingSeconds=$(( remainingSeconds - readSeconds ))
    if [ "$pressedKey" == " " ]
    then
      echo ""
      echo -n "Paused ($remainingSeconds seconds remaining, press <SPACE> to resume)"
      pressedKey=""
      while [ "$pressedKey" != " " ]
      do
        IFS= read -n1 pressedKey
      done
      echo ""
      echo "Resumed"
    fi
  done
  echo ""
}

warningWithPausableTimeout 10 "Program will end in"
echo "end."

可更新倒計時更新同一行

這是一個類似於第一個倒計時的倒計時,但它只需要一行。 依賴於echo -e來擦除和覆蓋先前打印的消息。

# A pausable countdown that repeatedly updates the same line.
#
# Repeatedly prints the message, the remaining time, and the state of
# the countdown, overriding the previously printed messages.
#
# @param $1 number of seconds for the countdown
# @param $2 message
singleLinePausableCountdown() {
  local remainingSeconds="$1"
  local message="$2"
  local state="run"
  local stateMessage=""
  local pressedKey=""
  while (( $remainingSeconds > 0 ))
  do
    if [ "$state" == "run" ]
    then
      stateMessage="[$remainingSeconds sec] Running, press <SPACE> to pause"
    else
      stateMessage="[$remainingSeconds sec] Paused, press <SPACE> to continue"
    fi
    echo -n "$message $stateMessage"
    pressedKey=""
    if [ "$state" == "run" ]
    then 
      IFS= read -n1 -t 1 pressedKey
      if [ "$pressedKey" == " " ]
      then
        state="pause"
      else 
        remainingSeconds=$(( remainingSeconds - 1 ))
      fi
    else
      IFS= read -n1 pressedKey
      if [ "$pressedKey" == " " ]
      then
        state="run"
      fi
    fi
    echo -ne "\033[1K\r"
  done
  echo "$message [Done]"
}

如果線條長於控制台寬度(它不會完全擦除線條),則可能會出現奇怪的情況。


對於試圖制作某人的任何人都沒有收集提示。 類似:

  • IFS= read -n1讀取單個字符
  • read -t <seconds>設置read超時。 超時到期后, read非零的退出,並將變量設置為空。
  • Magic bash內置變量$SECONDS測量腳本開始的時間。
  • 如果使用echo -n打印了一行,則可以使用echo -ne "\\033[1K\\r"刪除並重置該行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM