Linuxシェルスクリプトではい/いいえ/キャンセルの入力を求めるにはどうすればよいですか?


1443

シェルスクリプトでの入力を一時停止し、ユーザーに選択を求めます。
標準YesNoまたはCancelタイプの質問。
典型的なbashプロンプトでこれを行うにはどうすればよいですか?


11
ちょうど注:プロンプトの規則では、[yn]オプションを提示すると、大文字で始まるものがデフォルトに[Yn]なります。つまり、デフォルトは「yes」になり、[yN]デフォルトは「no」になります。ux.stackexchange.com/a/40445/43532を
Tyzoid

ZSHからここに来る人は誰でも、コマンドを使用してプロンプトを表示する方法については、この回答を参照readしてください
smac89

回答:


1619

シェルプロンプトでユーザー入力を取得する最も簡単で最も広く利用可能な方法は、readコマンドです。その使用法を説明する最良の方法は簡単なデモです:

while true; do
    read -p "Do you wish to install this program?" yn
    case $yn in
        [Yy]* ) make install; break;;
        [Nn]* ) exit;;
        * ) echo "Please answer yes or no.";;
    esac
done

Steven Huwigによって指摘された別の方法は、Bashのコマンドです。以下は、同じ例です。selectselect

echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
    case $yn in
        Yes ) make install; break;;
        No ) exit;;
    esac
done

ではselect、あなたが入力をサニタイズする必要はありません-それは、利用可能な選択肢が表示され、あなたはあなたの選択に対応する番号を入力します。また、自動的にループするため、while true無効な入力があった場合にループを再試行する必要はありません。

また、レアグリスは、彼女の回答で要求言語にとらわれない方法を示しました。私の最初の例を、複数の言語に対応するように変更すると、次のようになります。

set -- $(locale LC_MESSAGES)
yesptrn="$1"; noptrn="$2"; yesword="$3"; noword="$4"

while true; do
    read -p "Install (${yesword} / ${noword})? " yn
    case $yn in
        ${yesptrn##^} ) make install; break;;
        ${noptrn##^} ) exit;;
        * ) echo "Answer ${yesword} / ${noword}.";;
    esac
done

明らかに、他の通信文字列は未翻訳のままです(Install、Answer)。これはより完全な翻訳で対処する必要がありますが、部分的な翻訳でも多くの場合に役立ちます。

最後に、F。ハウリによるすばらしい答えをチェックしてください。


31
OS X LeopardでBashを使用して、「いいえ」を選択したときにタブを閉じないように変更exitbreakました。
Trey Piepmeier、2009

1
これは、はいまたはいいえより長いオプションでどのように機能しますか?case句では、次のように記述しますか?プログラムをインストールし、その後は何もしない)make install; ブレーク;
Shawn

1
使い方は@Shawn 読んで、あなたは、もちろん、bashシェルのswitch文でサポートされている任意のグロブや正規表現パターンを使用することができます。一致するものが見つかるまで、入力したテキストをパターンに一致させるだけです。selectを使用すると、選択肢のリストがコマンドに渡され、ユーザーに表示されます。リストの項目は、好きなだけ長くしたり短くしたりできます。包括的な使用方法については、manページを確認することをお勧めします。
Myrddin Emrys、2012

3
なぜそこにあるbreakではselect何のループが存在しない場合は?
Jayen、2014年

1
@akostadinov編集履歴をもっと読むべきです。あなたは正しい、私は間違っている、そして私はジェヤンのアドバイスに従ってバグを導入した、ハァッ。バグは後で修正されましたが、編集内容があまりにも離れていたため、変更したことさえ覚えていませんでした。
Myrddin Emrys 14年

527

1つの一般的な質問に対して少なくとも5つの回答。

応じて

  • 準拠:汎用の貧弱なシステムで動作する可能性があります 環境
  • 特定:いわゆるバシズムの使用

そしてあなたが望むなら

  • 単純な「インライン」の質問/回答(一般的なソリューション)
  • のようなかなりフォーマットされたインターフェース またはlibgtkまたはlibqtを使用したよりグラフィカルな...
  • 強力なreadline履歴機能を使用する

1. POSIXジェネリックソリューション

read次のコマンドを続けて使用できますif ... then ... else

echo -n "Is this a good question (y/n)? "
read answer

# if echo "$answer" | grep -iq "^y" ;then

if [ "$answer" != "${answer#[Yy]}" ] ;then
    echo Yes
else
    echo No
fi

Adam Katzのコメントのおかげで:上記のテストをより移植性が高く、1つのフォークを回避するテストに置き換えました:)

POSIX、ただし単一の主要機能

しかし、ユーザーにを押したくない場合はReturn、次のように書くことができます。

編集: @JonathanLefflerが正しく示唆しているように、sttyの構成を保存することは、単にそれらを正気にさせるよりも良いかもしれません。)

echo -n "Is this a good question (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty
if echo "$answer" | grep -iq "^y" ;then
    echo Yes
else
    echo No
fi

注:これは以下でテストされました そして

同じですが、yまたはを明示的に待っていnます:

#/bin/sh
echo -n "Is this a good question (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo
answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done )
stty $old_stty_cfg
if echo "$answer" | grep -iq "^y" ;then
    echo Yes
else
    echo No
fi

専用ツールを使用する

使用して構築された多くのツールがありlibncurseslibgtklibqtまたは他のグラフィックライブラリが。たとえば、次のように使用しwhiptailます。

if whiptail --yesno "Is this a good question" 20 60 ;then
    echo Yes
else
    echo No
fi

システムによっては、whiptail別の同様のツールに置き換える必要がある場合があります。

dialog --yesno "Is this a good question" 20 60 && echo Yes

gdialog --yesno "Is this a good question" 20 60 && echo Yes

kdialog --yesno "Is this a good question" 20 60 && echo Yes

ここで、20はダイアログボックスの高さ(行数)および60はダイアログボックスの幅です。これらのツールはすべてほぼ同じ構文を持っています。

DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...

2. Bash固有のソリューション

基本的なインライン方式

read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
    y|Y )
        echo Yes
    ;;
    * )
        echo No
    ;;
esac

私は使用したいcaseのでyes | ja | si | oui、必要に応じてテストすることもできます...

単一の主要機能に沿っ

bashの下では、readコマンドの対象となる入力の長さを指定できます。

read -n 1 -p "Is this a good question (y/n)? " answer

bashでは、readコマンドはタイムアウトパラメータを受け入れます。これは便利な場合があります。

read -t 3 -n 1 -p "Is this a good question (y/n)? " answer
[ -z "$answer" ] && answer="Yes"  # if 'yes' have to be default choice

3. 専用ツールの秘訣

単純なyes - no目的を超えた、より洗練されたダイアログボックス:

dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe

プログレスバー:

dialog --gauge "Filling the tank" 20 60 0 < <(
    for i in {1..100};do
        printf "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $i -1 $i
        sleep .033
    done
) 

小さなデモ:

#!/bin/sh
while true ;do
    [ -x "$(which ${DIALOG%% *})" ] || DIALOG=dialog
    DIALOG=$($DIALOG --menu "Which tool for next run?" 20 60 12 2>&1 \
            whiptail       "dialog boxes from shell scripts" >/dev/tty \
            dialog         "dialog boxes from shell with ncurses" \
            gdialog        "dialog boxes from shell with Gtk" \
            kdialog        "dialog boxes from shell with Kde" ) || exit
    clear;echo "Choosed: $DIALOG."
    for i in `seq 1 100`;do
        date +"`printf "XXX\n%d\n%%a %%b %%T progress: %d\nXXX\n" $i $i`"
        sleep .0125
      done | $DIALOG --gauge "Filling the tank" 20 60 0
    $DIALOG --infobox "This is a simple info box\n\nNo action required" 20 60
    sleep 3
    if $DIALOG --yesno  "Do you like this demo?" 20 60 ;then
        AnsYesNo=Yes; else AnsYesNo=No; fi
    AnsInput=$($DIALOG --inputbox "A text:" 20 60 "Text here..." 2>&1 >/dev/tty)
    AnsPass=$($DIALOG --passwordbox "A secret:" 20 60 "First..." 2>&1 >/dev/tty)
    $DIALOG --textbox /etc/motd 20 60
    AnsCkLst=$($DIALOG --checklist "Check some..." 20 60 12 \
        Correct "This demo is useful"        off \
        Fun        "This demo is nice"        off \
        Strong        "This demo is complex"        on 2>&1 >/dev/tty)
    AnsRadio=$($DIALOG --radiolist "I will:" 20 60 12 \
        " -1" "Downgrade this answer"        off \
        "  0" "Not do anything"                on \
        " +1" "Upgrade this anser"        off 2>&1 >/dev/tty)
    out="Your answers:\nLike: $AnsYesNo\nInput: $AnsInput\nSecret: $AnsPass"
    $DIALOG --msgbox "$out\nAttribs: $AnsCkLst\nNote: $AnsRadio" 20 60
  done

もっとサンプル?見ていたUSBデバイスの選択のためです:whiptailを使用し、USBリムーバブル記憶域セレクタを:USBKeyChooser

5. readlineの履歴を使用する

例:

#!/bin/bash

set -i
HISTFILE=~/.myscript.history
history -c
history -r

myread() {
    read -e -p '> ' $1
    history -s ${!1}
}
trap 'history -a;exit' 0 1 2 3 6

while myread line;do
    case ${line%% *} in
        exit )  break ;;
        *    )  echo "Doing something with '$line'" ;;
      esac
  done

これは、ファイルが作成され.myscript.history、あなたに$HOMEあなたのような、readlineの歴史コマンドを使用することができますよりも、ディレクトリUpDownCtrl+ rなどがあります。


4
sttyは、-g使用するためのオプションが用意されていますold_stty=$(stty -g); stty raw -echo; …; stty "$old_stty"。これにより、設定が見つかったとおりに復元されstty -saneます。これは、と同じ場合も同じでない場合もあります。
Jonathan Leffler、2015

3
read answerスペースや改行の前にバックスラッシュを解釈し、それ以外の場合はめったに意図しないものを取り除きます。read -r answer代わりにSC2162に従って使用してください。
vlfig 2017年

1
「readlineの履歴を使用する」方法は、OPの質問には非常に不適切です。とにかくそれを含めてよかったです。私はそのパターンで更新するつもりのはるかに複雑なスクリプトを何十も持っています!
ブルーノブロノスキー

5
casebashと同様にPOSIXにも使用できます(bash部分文字列ではなくワイルドカード条件を使用しますcase $answer in; [Yy]* ) echo Yes ;;)が、代わりに条件ステートメントを使用すること[ "$answer" != "${answer#[Yy]}" ]をお勧めしますecho "$answer" | grep -iq ^y。移植性が高く(GNU以外の一部のgrepは-q正しく実装されない)、システムコールがありません。 ${answer#[Yy]}パラメータ展開を使用してを削除するYy、の最初から、$answerどちらかが存在する場合に不等式を引き起こします これは、どのPOSIXシェル(ダッシュ、ksh、bash、zsh、busyboxなど)でも機能します。
Adam Katz

1
@CarterPapeはい、これは冗談でした!しかし、この詳細な回答では、多くのヒントが見つかる可能性があります(2つ目のポイントは、少なくとも3つの異なる方法が存在します)。そして...約5年前から、私のカウント方法について最初に説明します!
;

349
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"

25
DOSの「はい、いいえ、キャンセル」ダイアログの機能の一部しか実装していないため、同意しません。実装に失敗した部分は入力チェックです...有効な回答が受信されるまでループします。
Myrddin Emrys

1
(元の質問のタイトルは「Linuxシェルスクリプトで入力を求めるにはどうすればよいですか?」)
ピストス

8
ただし、元の質問の説明は変更されておらず、常に「はい/いいえ/キャンセル」プロンプトへの応答を求められました。タイトルは元のタイトルよりも明確になるように更新されましたが、質問の説明は常に(私の意見では)明確でした。
Myrddin Emrys 2017

165

組み込みの読み取りコマンドを使用できます。-pオプションを使用して、ユーザーに質問を促します。

BASH4以降、これを使用-iして回答を提案できます。

read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
echo $FILEPATH

(ただし、「readline」オプション-eを使用して、矢印キーで行を編集できるようにしてください)

「はい/いいえ」のロジックが必要な場合は、次のようなことができます。

read -e -p "
List the content of your home dir ? [Y/n] " YN

[[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/

6
FILEPATHは、選択した変数名であり、コマンドプロンプトへの回答で設定されることに注意してください。したがってvlc "$FILEPATH"、たとえばを実行すると、vlcそのファイルが開かれます。
ケンシャープ

-e2番目の例(単純なはい/いいえ)の利点は何ですか?
JBallin

-e -p代わりに使用する理由はあり-epますか?
JBallin

1
なければ-eフラグ/オプション、あなたは(実装に依存)かもしれない「Y」を入力し、[あなたの心を変更し、「N」(またはそのことについて何か)と交換することができます。コマンドを文書化するとき、オプションを個別にリストすることは、数ある理由の中でも特に、読みやすさ/明確さのために優れています。
yPhil 2018年

109

バッシュはこの目的のために選択しています

select result in Yes No Cancel
do
    echo $result
done

18
+1非常にシンプルなソリューション。唯一のこと:これはプロンプトを出し、プロンプトを出し、プロンプトを出します... exit内部を追加するまで:)
kaiser

5
(カイザー:そこから抜けるには、EOT:を入力するだけですCtrl-D。しかし、もちろん、それを使用する実際のコードでは、本文に区切りまたは出口が必要です。)
Zorawar

12
これは、yを入力できるようにするか、N、though.Youは1 2または3を入力して、選択しないだろう
djjeck

3
exitスクリプトをすべてbreak終了し、現在のループのみを終了します(a whileまたはcaseループの場合)
wranvaud

57
read -p "Are you alright? (y/n) " RESP
if [ "$RESP" = "y" ]; then
  echo "Glad to hear it"
else
  echo "You need more bash programming"
fi

36

これが私がまとめたものです:

#!/bin/sh

promptyn () {
    while true; do
        read -p "$1 " yn
        case $yn in
            [Yy]* ) return 0;;
            [Nn]* ) return 1;;
            * ) echo "Please answer yes or no.";;
        esac
    done
}

if promptyn "is the sky blue?"; then
    echo "yes"
else
    echo "no"
fi

私は初心者なので、これを塩の粒と一緒に服用しますが、うまくいくようです。


9
あなたが変更した場合case $yn incase ${yn:-$2} in、あなたはデフォルト値として第二引数を使用することができ、YまたはN.
jchook

1
または変更case $yncase "${yn:-Y}"デフォルトとしてyesを持っている
rubo77

35
inquire ()  {
  echo  -n "$1 [y/n]? "
  read answer
  finish="-1"
  while [ "$finish" = '-1' ]
  do
    finish="1"
    if [ "$answer" = '' ];
    then
      answer=""
    else
      case $answer in
        y | Y | yes | YES ) answer="y";;
        n | N | no | NO ) answer="n";;
        *) finish="-1";
           echo -n 'Invalid response -- please reenter:';
           read answer;;
       esac
    fi
  done
}

... other stuff

inquire "Install now?"

...

1
コードのフォーマットを維持するために、各行の先頭に4つのスペースを入れます。
Jouni K.Seppänen、

10
ケーススイッチがハードコーディングされている場合、inquire()のパラメーターとして「y」と「n」を指定するのはなぜですか?それは誤用を求めているだけです。これらは固定パラメーターであり、変更できないため、2行目のエコーは次のようになります。echo -n "$ 1 [Y / N]?"これらは変更できないため、指定しないでください。
Myrddin Emrys、

1
@MyrddinEmrysコメントについて詳しく教えてください。または、記事へのリンクまたはいくつかのキーワードを投稿して、自分で調査できるようにします。
Mateusz Piotrowski 2016年

4
@MateuszPiotrowskiコメントを書いてから回答が編集、改善されました。上の[12月23日編集]リンクをクリックすると、この回答の過去のバージョンをすべて表示できます。2008年には、コードはかなり異なっていました。
Myrddin Emrys 2016年

27

あなたが欲しい:

  • Bash組み込みコマンド(つまり、移植可能)
  • TTYを確認する
  • デフォルトの回答
  • タイムアウト
  • 色付きの質問

スニペット

do_xxxx=y                      # In batch mode => Default is Yes
[[ -t 0 ]] &&                  # If TTY => Prompt the question
read -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx  # Store the answer in $do_xxxx
if [[ $do_xxxx =~ ^(y|Y|)$ ]]  # Do if 'y' or 'Y' or empty
then
    xxxx
fi

解説

  • [[ -t 0 ]] && read ...=> readTTYの場合にコマンドを呼び出す
  • read -n 1 => 1文字待つ
  • $'\e[1;32m ... \e[0m '=>緑で印刷
    (白/黒の両方の背景で判読できるため、緑は問題ありません)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash正規表現

タイムアウト=>デフォルトの回答は「いいえ」です

do_xxxx=y
[[ -t 0 ]] && {                   # Timeout 5 seconds (read -t 5)
read -t 5 -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx ||  # read 'fails' on timeout
do_xxxx=n ; }                     # Timeout => answer No
if [[ $do_xxxx =~ ^(y|Y|)$ ]]
then
    xxxx
fi

26

最小限の行数でこれを実現する最も簡単な方法は、次のとおりです。

read -p "<Your Friendly Message here> : y/n/cancel" CONDITION;

if [ "$CONDITION" == "y" ]; then
   # do something here!
fi

これifは一例にすぎません。この変数の処理方法はユーザー次第です。


20

次のreadコマンドを使用します。

echo Would you like to install? "(Y or N)"

read x

# now check if $x is "y"
if [ "$x" = "y" ]; then
    # do something here!
fi

そして、あなたが必要とする他のすべてのもの


17

このソリューションは、単一の文字を読み取り、yes応答で関数を呼び出します。

read -p "Are you sure? (y/n) " -n 1
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    do_something      
fi

2
@Javエコーは、応答の後に改行を出力します。それがなければ、次に印刷されるものは、同じ行の応答の直後に表示されます。を削除しechoて、自分の目で確かめてください。
デニス

13

ncursesのような素敵な入力ボックスを取得するには、次のようなコマンドダイアログを使用します。

#!/bin/bash
if (dialog --title "Message" --yesno "Want to do something risky?" 6 25)
# message box will have the size 25x6 characters
then 
    echo "Let's do something risky"
    # do something risky
else 
    echo "Let's stay boring"
fi

ダイアログパッケージは、少なくともSUSE Linuxではデフォルトでインストールされます。次のようになります。 アクションの「dialog」コマンド


12
read -e -p "Enter your choice: " choice

この-eオプションを使用すると、ユーザーは矢印キーを使用して入力を編集できます。

入力として提案を使用する場合:

read -e -i "yes" -p "Enter your choice: " choice

-i オプションは、示唆的な入力を出力します。


ヤップ、-e -iSH(Bourneシェル)で仕事をしませんが、質問は...特にタグ付きのbashをである
Jahid

12

のデフォルトREPLYを使用しreadて小文字に変換し、式を使用して変数のセットと比較できます。
スクリプトはja/ si/ もサポートしますoui

read -rp "Do you want a demo? [y/n/c] "

[[ ${REPLY,,} =~ ^(c|cancel)$ ]] && { echo "Selected Cancel"; exit 1; }

if [[ ${REPLY,,} =~ ^(y|yes|j|ja|s|si|o|oui)$ ]]; then
   echo "Positive"
fi

12

POSIXシェルでロケール対応の「はい/いいえ選択」を処理することは可能です。LC_MESSAGESwitchは、ロケールカテゴリのエントリを使用して、入力に一致する既製のRegExパターンと、ローカライズされたはいいいえの文字列を提供します。

#!/usr/bin/env sh

# Getting LC_MESSAGES values into variables
# shellcheck disable=SC2046 # Intended IFS splitting
IFS='
' set -- $(locale LC_MESSAGES)

yesexpr="$1"
noexpr="$2"
yesstr="$3"
nostr="$4"
messages_codeset="$5" # unused here, but kept as documentation

# Display Yes / No ? prompt into locale
echo "$yesstr / $nostr ?"

# Read answer
read -r yn

# Test answer
case "$yn" in
# match only work with the character class from the expression
  ${yesexpr##^}) echo "answer $yesstr" ;;
  ${noexpr##^}) echo "answer $nostr" ;;
esac

編集:@Urhixidur彼のコメントで述べたように:

残念ながら、POSIXは最初の2つ(yesexprとnoexpr)のみを指定しています。Ubuntu 16では、yesstrとnostrは空です。

参照:https : //www.ee.ryerson.ca/~courses/ele709/susv4/xrat/V4_xbd_chap07.html#tag_21_07_03_06

LC_MESSAGES

ロケールキーワードとし、LANGINFO項目は、以前のユーザー肯定と否定の応答を一致させるために使用されました。POSIX.1-2008では、、、、および拡張正規表現は、それらを交換しました。アプリケーションは、一般的なロケールベースのメッセージング機能を使用して、サンプルの必要な応答を含むプロンプトメッセージを発行する必要があります。yesstrnostrYESSTRNOSTRyesexprnoexprYESEXPRNOEXPR

別の方法として、Bashの方法でロケールを使用します。

#!/usr/bin/env bash

IFS=$'\n' read -r -d '' yesexpr noexpr _ < <(locale LC_MESSAGES)

printf -v yes_or_no_regex "(%s)|(%s)" "$yesexpr" "$noexpr"

printf -v prompt $"Please answer Yes (%s) or No (%s): " "$yesexpr" "$noexpr"

declare -- answer=;

until [[ "$answer" =~ $yes_or_no_regex ]]; do
  read -rp "$prompt" answer
done

if [[ -n "${BASH_REMATCH[1]}" ]]; then
  echo $"You answered: Yes"
else
  echo $"No, was your answer."
fi

答えは、ロケール環境が提供する正規表現を使用して照合されます。

残りのメッセージを翻訳するには、を使用bash --dump-po-strings scriptnameしてローカライズ用のpo文字列を出力します。

#: scriptname:8
msgid "Please answer Yes (%s) or No (%s): "
msgstr ""
#: scriptname:17
msgid "You answered: Yes"
msgstr ""
#: scriptname:19
msgid "No, was your answer."
msgstr ""

言語にとらわれないオプションの追加が大好きです。よくやった。
Myrddin Emrys、

1
残念ながら、POSIXは最初の2つ(yesexprとnoexpr)のみを指定しています。Ubuntu 16では、yesstrとnostrは空です。
Urhixidur

1
ちょっと待って!悪いニュースがあります!bashのcaseステートメント式は正規ではなく、ファイル名式です。したがって、Ubuntu 16のyesexprおよびnoexpr(それぞれ「^ [yY]。*」および「^ [nN]。*」)は、埋め込まれたピリオドのために完全に失敗します。正規表現では、「。*」は「改行以外の文字、0回以上」を意味します。しかし、caseステートメントでは、それは文字通りの "。"です。その後に任意の数の文字が続きます。
Urhixidur

1
最後に行うことができるよう最善yesexprnoexprシェル環境では、バッシュの特定の正規表現のマッチングで使用されるif [[ "$yn" =~ $yesexpr ]]; then echo $"Answered yes"; else echo $"Answered no"; fi
Léaのグリ

10

単一のキープレスのみ

これは、より長く、再利用可能なモジュール式のアプローチです。

  • 0= yesと1= noを返します
  • Enterキーを押す必要はありません-単一の文字のみ
  • 押しenterてデフォルトの選択を受け入れることができます
  • デフォルトの選択を無効にして強制的に選択できます
  • との両方zshで機能しbashます。

Enterキーを押すと、デフォルトで「いいえ」になります。

Nが大文字であることに注意してください。ここではEnterが押され、デフォルトを受け入れます。

$ confirm "Show dangerous command" && echo "rm *"
Show dangerous command [y/N]?

また、これ[y/N]?は自動的に追加されました。デフォルトの「no」は受け入れられるため、何もエコーされません。

有効な応答が得られるまで再プロンプトを表示します。

$ confirm "Show dangerous command" && echo "rm *"
Show dangerous command [y/N]? X
Show dangerous command [y/N]? y
rm *

Enterキーを押すと、デフォルトで「はい」になります。

Yが大文字であることに注意してください:

$ confirm_yes "Show dangerous command" && echo "rm *"
Show dangerous command [Y/n]?
rm *

上記では、Enterキーを押しただけなので、コマンドが実行されました。

デフォルトなしenter-必須yまたはn

$ get_yes_keypress "Here you cannot press enter. Do you like this [y/n]? "
Here you cannot press enter. Do you like this [y/n]? k
Here you cannot press enter. Do you like this [y/n]?
Here you cannot press enter. Do you like this [y/n]? n
$ echo $?
1

ここでは1、falseが返されました。この下位レベルの関数では、独自の[y/n]?プロンプトを提供する必要があることに注意してください。

コード

# Read a single char from /dev/tty, prompting with "$*"
# Note: pressing enter will return a null string. Perhaps a version terminated with X and then remove it in caller?
# See https://unix.stackexchange.com/a/367880/143394 for dealing with multi-byte, etc.
function get_keypress {
  local REPLY IFS=
  >/dev/tty printf '%s' "$*"
  [[ $ZSH_VERSION ]] && read -rk1  # Use -u0 to read from STDIN
  # See https://unix.stackexchange.com/q/383197/143394 regarding '\n' -> ''
  [[ $BASH_VERSION ]] && </dev/tty read -rn1
  printf '%s' "$REPLY"
}

# Get a y/n from the user, return yes=0, no=1 enter=$2
# Prompt using $1.
# If set, return $2 on pressing enter, useful for cancel or defualting
function get_yes_keypress {
  local prompt="${1:-Are you sure [y/n]? }"
  local enter_return=$2
  local REPLY
  # [[ ! $prompt ]] && prompt="[y/n]? "
  while REPLY=$(get_keypress "$prompt"); do
    [[ $REPLY ]] && printf '\n' # $REPLY blank if user presses enter
    case "$REPLY" in
      Y|y)  return 0;;
      N|n)  return 1;;
      '')   [[ $enter_return ]] && return "$enter_return"
    esac
  done
}

# Credit: http://unix.stackexchange.com/a/14444/143394
# Prompt to confirm, defaulting to NO on <enter>
# Usage: confirm "Dangerous. Are you sure?" && rm *
function confirm {
  local prompt="${*:-Are you sure} [y/N]? "
  get_yes_keypress "$prompt" 1
}    

# Prompt to confirm, defaulting to YES on <enter>
function confirm_yes {
  local prompt="${*:-Are you sure} [Y/n]? "
  get_yes_keypress "$prompt" 0
}

私が代わりにoututのI上で、このスクリプトをテストしたときだ、Show dangerous command [y/N]? [y/n]?Show dangerous command [Y/n]? [y/n]?
イリアスカリム・

@IliasKarimに感謝します。今すぐ修正しました。
トム・ヘイル

9

そのような古い投稿に投稿して申し訳ありません。数週間前、私は同様の問題に直面していました。私の場合、オンラインインストーラースクリプト内でも機能するソリューションが必要でした。例:curl -Ss https://raw.github.com/_____/installer.sh | bash

使用read yesno < /dev/ttyするとうまくいきます:

echo -n "These files will be uploaded. Is this ok? (y/n) "
read yesno < /dev/tty

if [ "x$yesno" = "xy" ];then

   # Yes
else

   # No
fi

これが誰かを助けることを願っています。


これの重要な部分は入力検証です。私は受け入れるように私の最初の例を適応と思うttyあなたはあなたのためにも行われ、また、不正な入力にループし得ただろうでしたように、入力を(バッファ内の数文字を想像;にあなたの方法は、ユーザーを強制しないだろう、常に何を選択します)。
Myrddin Emrys、2014年

7

そのような単純なユーザー入力に対して複数行のエコーメニューを表示する回答を誰も投稿していないことに気付いたので、ここで私の取り組みを説明します。

#!/bin/bash

function ask_user() {    

echo -e "
#~~~~~~~~~~~~#
| 1.) Yes    |
| 2.) No     |
| 3.) Quit   |
#~~~~~~~~~~~~#\n"

read -e -p "Select 1: " choice

if [ "$choice" == "1" ]; then

    do_something

elif [ "$choice" == "2" ]; then

    do_something_else

elif [ "$choice" == "3" ]; then

    clear && exit 0

else

    echo "Please select 1, 2, or 3." && sleep 3
    clear && ask_user

fi
}

ask_user

この方法は、誰かが便利で時間を節約できることを期待して投稿されました。


4

複数選択バージョン:

ask () {                        # $1=question $2=options
    # set REPLY
    # options: x=..|y=..
    while $(true); do
        printf '%s [%s] ' "$1" "$2"
        stty cbreak
        REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null)
        stty -cbreak
        test "$REPLY" != "$(printf '\n')" && printf '\n'
        (
            IFS='|'
            for o in $2; do
                if [ "$REPLY" = "${o%%=*}" ]; then
                    printf '\n'
                    break
                fi
            done
        ) | grep ^ > /dev/null && return
    done
}

例:

$ ask 'continue?' 'y=yes|n=no|m=maybe'
continue? [y=yes|n=no|m=maybe] g
continue? [y=yes|n=no|m=maybe] k
continue? [y=yes|n=no|m=maybe] y
$

(スクリプト内で)に設定さREPLYyます。


4

ダイアログ使用することをお勧めします...

Linux見習い:ダイアログを使用してBashシェルスクリプトを改善する

ダイアログコマンドを使用すると、シェルスクリプトでウィンドウボックスを使用して、よりインタラクティブに使用できます。

シンプルで使いやすいです。gdialogと呼ばれるgnomeバージョンもあり、まったく同じパラメーターを使用しますが、XでGUIスタイルを示します。


カジュアルな読者の方は、こちらのダイアログコマンドを使用してスニペットをご覧ください:stackoverflow.com/a/22893526/363573
Stephan

4

@Markおよび@Myrddinの回答に触発されて、私はこの関数をユニバーサルプロンプト用に作成しました

uniprompt(){
    while true; do
        echo -e "$1\c"
        read opt
        array=($2)
        case "${array[@]}" in  *"$opt"*) eval "$3=$opt";return 0;; esac
        echo -e "$opt is not a correct value\n"
    done
}

次のように使用します。

unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection
echo "$selection"

4

より一般的なものは:

function menu(){
    title="Question time"
    prompt="Select:"
    options=("Yes" "No" "Maybe")
    echo "$title"
    PS3="$prompt"
    select opt in "${options[@]}" "Quit/Cancel"; do
        case "$REPLY" in
            1 ) echo "You picked $opt which is option $REPLY";;
            2 ) echo "You picked $opt which is option $REPLY";;
            3 ) echo "You picked $opt which is option $REPLY";;
            $(( ${#options[@]}+1 )) ) clear; echo "Goodbye!"; exit;;
            *) echo "Invalid option. Try another one.";continue;;
         esac
     done
     return
}

3

これを行う簡単な方法の1つは、xargs -por gnuを使用することparallel --interactiveです。

最後に実行するyessesを収集するのではなく、他の対話型のunixコマンドのようにプロンプ​​トの直後に各コマンドを実行するため、xargsの動作は少し良くなっています。(必要なものを通過した後、Ctrl-Cを押すことができます。)

例えば、

echo *.xml | xargs -p -n 1 -J {} mv {} backup/

悪くはありませんが、xargs --interactiveはいまたはいいえに限定されます。それで十分であれば十分ですが、私の最初の質問では、3つの結果が考えられる例を示しました。私はそれがストリーミング可能であることを本当に気に入っています。多くの一般的なシナリオは、パイプで接続する機能の恩恵を受けます。
Myrddin Emrys 2015

そうですか。私の考えでは、「キャンセル」は、Ctrl-Cを介してサポートされるそれ以降のすべての実行を単に停止することを意味しましたが、キャンセル(またはいいえ)でより複雑な何かを行う必要がある場合、これでは不十分です。
Joshua Goldberg

3

1行のコマンドの友として、次のコマンドを使用しました。

while [ -z $prompt ]; do read -p "Continue (y/n)?" choice;case "$choice" in y|Y ) prompt=true; break;; n|N ) exit 0;; esac; done; prompt=;

長い形式で書かれ、次のように機能します。

while [ -z $prompt ];
  do read -p "Continue (y/n)?" choice;
  case "$choice" in
    y|Y ) prompt=true; break;;
    n|N ) exit 0;;
  esac;
done;
prompt=;

プロンプト変数の使用を明確にできますか?ワンライナーの後にふき取られたように見えるので、どのようにしてラインを使って何をしますか?
Myrddin Emrys、2015年

whileループの後にプロンプ​​トがワイプされます。プロンプト変数を後で初期化したいので(ステートメントをより頻繁に使用しているため)。シェルスクリプトにこの行があると、y | Yが入力された場合にのみ進み、n | Nが入力された場合に終了するか、他のすべての入力を繰り返し要求します。
ccDict 2015年

3

私はcaseそのようなシナリオでステートメントを数回使用しました。ケースステートメントを使用することは、それを実行するための良い方法です。whileecapsulatesループ、caseブール条件を利用するブロックは、プログラムの一層の制御を保持し、および他の多くの要件を満たすために実装することができます。すべての条件が満たされた後、break制御をプログラムのメイン部分に戻すを使用できます。また、他の条件を満たすために、当然のことながら、条件付きステートメントを追加して、制御構造(caseステートメントと可能なwhileループ)に含めることができます。

caseステートメントを使用してリクエストを満たす例

#! /bin/sh 

# For potential users of BSD, or other systems who do not
# have a bash binary located in /bin the script will be directed to
# a bourne-shell, e.g. /bin/sh

# NOTE: It would seem best for handling user entry errors or
# exceptions, to put the decision required by the input 
# of the prompt in a case statement (case control structure), 

echo Would you like us to perform the option: "(Y|N)"

read inPut

case $inPut in
    # echoing a command encapsulated by 
    # backticks (``) executes the command
    "Y") echo `Do something crazy`
    ;;
    # depending on the scenario, execute the other option
    # or leave as default
    "N") echo `execute another option`
    ;;
esac

exit

3

はい/いいえ/キャンセル

関数

#!/usr/bin/env bash
@confirm() {
  local message="$*"
  local result=''

  echo -n "> $message (Yes/No/Cancel) " >&2

  while [ -z "$result" ] ; do
    read -s -n 1 choice
    case "$choice" in
      y|Y ) result='Y' ;;
      n|N ) result='N' ;;
      c|C ) result='C' ;;
    esac
  done

  echo $result
}

使用法

case $(@confirm 'Confirm?') in
  Y ) echo "Yes" ;;
  N ) echo "No" ;;
  C ) echo "Cancel" ;;
esac

クリーンなユーザー入力で確認

関数

#!/usr/bin/env bash
@confirm() {
  local message="$*"
  local result=3

  echo -n "> $message (y/n) " >&2

  while [[ $result -gt 1 ]] ; do
    read -s -n 1 choice
    case "$choice" in
      y|Y ) result=0 ;;
      n|N ) result=1 ;;
    esac
  done

  return $result
}

使用法

if @confirm 'Confirm?' ; then
  echo "Yes"
else
  echo "No"
fi

2
yn() {
  if [[ 'y' == `read -s -n 1 -p "[y/n]: " Y; echo $Y` ]];
  then eval $1;
  else eval $2;
  fi }
yn 'echo yes' 'echo no'
yn 'echo absent no function works too!'

これは複雑で壊れやすいようです。いかがですかyn(){ read -s -n 1 -p '[y/n]'; test "$REPLY" = "y" ; } yn && echo success || echo failure
tripleee

2

他者への対応:

BASH4で大文字と小文字を指定する必要はありません。varを小文字にするために ',,'を使用するだけです。また、私はコードを読み取りブロック内に配置して結果を取得し、読み取りブロックIMOの外部で処理することを強く嫌っています。また、IMOを終了するには「q」を含めます。最後に、「yes」と入力する理由は、-n1を使用してyを押すだけです。

例:ユーザーはy / nとqを押すだけで終了できます。

ans=''
while true; do
    read -p "So is MikeQ the greatest or what (y/n/q) ?" -n1 ans
    case ${ans,,} in
        y|n|q) break;;
        *) echo "Answer y for yes / n for no  or q for quit.";;
    esac
done

echo -e "\nAnswer = $ans"

if [[ "${ans,,}" == "q" ]] ; then
        echo "OK Quitting, we will assume that he is"
        exit 0
fi

if [[ "${ans,,}" == "y" ]] ; then
        echo "MikeQ is the greatest!!"
else
        echo "No? MikeQ is not the greatest?"
fi

0

このようなシナリオでは、ほとんどの場合、ユーザーが「yes」と入力し続けるまでスクリプトを実行し続ける必要があり、ユーザーが「no」と入力したときにのみ停止する必要があります。以下のスニペットは、これを達成するのに役立ちます!

#!/bin/bash
input="yes"
while [ "$input" == "yes" ]
do
  echo "execute script functionality here..!!"
  echo "Do you want to continue (yes/no)?"
  read input
done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.