簡體   English   中英

bash:在“選擇”提示中按下回車時從案例中選擇默認值

[英]bash: choose default from case when enter is pressed in a "select" prompt

我在這樣的 bash 腳本中提示問題:

optionsAudits=("Yep" "Nope")
    echo "Include audits?"
    select opt in "${optionsAudits[@]}"; do
        case $REPLY in
            1) includeAudits=true; break ;;
            2) includeAudits=false; break ;;
            "\n") echo "You pressed enter"; break ;; # <--- doesn't work
            *) echo "What's that?"; exit;;
        esac
    done

按下回車鍵時如何選擇默認選項? "\n"大小寫不捕獲回車鍵。

為了補充Aserre 的有用答案,它解釋了您的代碼問題並提供了有效的解決方法,包括背景信息和允許空輸入的通用、可重用的自定義select實現


背景資料

明確說明: select本身會忽略空輸入(只需按Enter )並簡單地重新提示- 用戶代碼甚至不會響應運行。

實際上, select使用空字符串向用戶代碼發出信號,表明輸入了無效的選擇
也就是說,如果輸出變量 - $opt ,在這種情況下為 int - 在select語句中為,則暗示用戶鍵入了無效的選擇索引。

輸出變量接收所選選項的文本——在本例中為“是'Yep''Nope'否”——而不是用戶鍵入的索引

(相比之下,您的代碼檢查$REPLY而不是輸出變量,它包含用戶鍵入的內容,這有效選擇的索引,但可能包含額外的前導和尾隨空格)。

請注意,如果您不想允許空輸入,您可以簡單地在提示文本中向用戶指示^C ( Ctrl+C ) 可用於中止提示


也接受空輸入的通用自定義select函數

以下函數密切模擬select的作用,同時還允許空輸入(只需按Enter )。 請注意,該函數會攔截無效輸入、打印警告並重新提示:

# Custom `select` implementation that allows *empty* input.
# Pass the choices as individual arguments.
# Output is the chosen item, or "", if the user just pressed ENTER.
# Example:
#    choice=$(selectWithDefault 'one' 'two' 'three')
selectWithDefault() {

  local item i=0 numItems=$# 

  # Print numbered menu items, based on the arguments passed.
  for item; do         # Short for: for item in "$@"; do
    printf '%s\n' "$((++i))) $item"
  done >&2 # Print to stderr, as `select` does.

  # Prompt the user for the index of the desired item.
  while :; do
    printf %s "${PS3-#? }" >&2 # Print the prompt string to stderr, as `select` does.
    read -r index
    # Make sure that the input is either empty or that a valid index was entered.
    [[ -z $index ]] && break  # empty input
    (( index >= 1 && index <= numItems )) 2>/dev/null || { echo "Invalid selection. Please try again." >&2; continue; }
    break
  done

  # Output the selected item, if any.
  [[ -n $index ]] && printf %s "${@: index:1}"

}

你可以這樣稱呼它:

# Print the prompt message and call the custom select function.
echo "Include audits (default is 'Nope')?"
optionsAudits=('Yep' 'Nope')
opt=$(selectWithDefault "${optionsAudits[@]}")

# Process the selected item.
case $opt in
  'Yep') includeAudits=true; ;;
  ''|'Nope') includeAudits=false; ;; # $opt is '' if the user just pressed ENTER
esac

讓函數本身處理默認邏輯的替代實現謝謝, RL-S

這個實現在兩個方面與上面的不同:

  • 它允許您在選項中指定一個默認值,方法是在它前面加上! ,否則第一選擇將成為默認值。 默認選項以尾隨[default]打印(並且沒有前導! )。 然后該函數將空輸入轉換為默認選擇。

  • 所選選項作為基於1索引而不是文本返回。 換句話說:您可以假設在函數返回時做出了有效的選擇,並且該選擇由它在給定選擇中的位置指示。

# Custom `select` implementation with support for a default choice
# that the user can make by pressing just ENTER.
# Pass the choices as individual arguments; e.g. `selectWithDefault Yes No``
# The first choice is the default choice, unless you designate
# one of the choices as the default with a leading '!', e.g.
# `selectWithDefault Yes !No`
# The default choice is printed with a trailing ' [default]'
# Output is the 1-based *index* of the selected choice, as shown
# in the UI.
# Example:
#    choice=$(selectWithDefault 'Yes|No|!Abort' )
selectWithDefault() {

  local item i=0 numItems=$# defaultIndex=0

  # Print numbered menu items, based on the arguments passed.
  for item; do         # Short for: for item in "$@"; do
    [[ "$item" == !* ]] && defaultIndex=$(( $i + 1)) && item="${item:1} [default]"
    printf '%s\n' "$((++i))) $item"
  done >&2 # Print to stderr, as `select` does.

  # Prompt the user for the index of the desired item.
  while :; do
    printf %s "${PS3-#? }" >&2 # Print the prompt string to stderr, as `select` does.
    read -r index
    # Make sure that the input is either empty or that a valid index was entered.
    [[ -z $index ]] && index=$defaultIndex && break  # empty input == default choice  
    (( index >= 1 && index <= numItems )) 2>/dev/null || { echo "Invalid selection. Please try again." >&2; continue; }
    break
  done

  # Output the selected *index* (1-based).
  printf $index

}

示例調用:

# Print the prompt message and call the custom select function,
# designating 'Abort' as the default choice.
echo "Include audits?"
ndx=$(selectWithDefault 'Yes' 'No', '!Abort')

case $ndx in
  1) echo "include";;
  2) echo "don't include";;
  3) echo "abort";;
esac

可選閱讀:原始代碼的更慣用版本

注意:這段代碼並沒有解決問題,但顯示了select語句的更慣用用法; 與原始代碼不同,如果做出了無效選擇,此代碼會重新顯示提示:

optionsAudits=("Yep" "Nope")
echo "Include audits (^C to abort)?"
select opt in "${optionsAudits[@]}"; do
    # $opt being empty signals invalid input.
    [[ -n $opt ]] || { echo "What's that? Please try again." >&2; continue; }
    break # a valid choice was made, exit the prompt.
done

case $opt in  # $opt now contains the *text* of the chosen option
  'Yep')
     includeAudits=true
     ;;
  'Nope') # could be just `*` in this case.
     includeAudits=false
     ;;
esac

筆記:

  • case語句被移出select語句,因為后者現在保證只能進行有效輸入。

  • case語句測試輸出變量( $opt ) 而不是原始用戶輸入 ( $REPLY ),並且該變量包含選擇文本,而不是它的索引

您的問題是由於select將忽略空輸入。 對於您的情況, read會更合適,但您將失去select提供的用於自動菜單創建的實用程序。

要模擬select的行為,您可以執行以下操作:

#!/bin/bash
optionsAudits=("Yep" "Nope")
while : #infinite loop. be sure to break out of it when a valid choice is made
do
    i=1
    echo "Include Audits?"
    #we recreate manually the menu here
    for o in  "${optionsAudits[@]}"; do
        echo "$i) $o"
        let i++
    done

    read reply
    #the user can either type the option number or copy the option text
    case $reply in
        "1"|"${optionsAudits[0]}") includeAudits=true; break;;
        "2"|"${optionsAudits[1]}") includeAudits=false; break;;
        "") echo "empty"; break;;
        *) echo "Invalid choice. Please choose an existing option number.";;
    esac
done
echo "choice : \"$reply\""

更新的答案:

echo "Include audits? 1) Yep, 2) Nope"
read ans
case $ans in
    Yep|1  )  echo "yes"; includeAudits=true; v=1 ;;
    Nope|2 )  echo "no"; includeAudits=false; v=2 ;;
    ""     )  echo "default - yes"; includeAudits=true; v=1 ;;
    *      )  echo "Whats that?"; exit ;;
esac

這接受"Yep""1""enter"以選擇“是”,並接受"Nope""2"表示否,並丟棄其他任何內容。 它還將 v 設置為 1 或 2,具體取決於用戶是否想要是或否。

這將滿足您的要求。

options=("option 1" "option 2");
while :
do
    echo "Select your option:"
    i=1;
    for opt in  "${options[@]}"; do
        echo "$i) $opt";
        let i++;
    done

    read reply
    case $reply in
        "1"|"${options[0]}"|"")
          doSomething1();
          break;;
        "2"|"${options[1]}")
          doSomething2();
          break;;
        *)
          echo "Invalid choice. Please choose 1 or 2";;
    esac
done

假設您的默認選項是Yep

#!/bin/bash
optionsAudits=("Yep" "Nope")
while : #infinite loop. be sure to break out of it when a valid choice is made
do
    i=1
    echo "Include Audits?: "
    #we recreate manually the menu here
    for o in  "${optionsAudits[@]}"; do
        echo "  $i) $o"
        let i++
    done

    read -rp "Audit option: " -iYep
    #the user can either type the option number or copy the option text
    case $REPLY in
        "1"|"${optionsAudits[0]}") includeAudits=true; break;;
        "2"|"${optionsAudits[1]}") includeAudits=false; break;;
        "") includeAudits=true; break;;
        *) echo "Invalid choice. Please choose an existing option number.";;
    esac
done
echo "choice : \"$REPLY\""
echo "includeAudits : \"$includeAudits\""

注意到這一行:

    read -rp "Audit option: " -eiYep

此外,我將$reply拉到$REPLY以便案件判決效果更好。

按下 ENTER 后,輸出現在看起來像這樣:

Include Audits?: 
  1) Yep
  2) Nope
Audit option: Yep
choice : ""
includeAudits : "true"
# 

作為對select的增強, read -eiYep將預先提供Yep默認值到輸入緩沖區中。

將默認值放在前面的唯一缺點是必須按退格鍵幾次才能輸入自己的答案。

暫無
暫無

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

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