Linux 쉘 스크립트에서 예 / 아니오 / 취소 입력을 프롬프트하는 방법은 무엇입니까?


1443

쉘 스크립트에서 입력을 일시 중지하고 사용자에게 선택을 요구합니다.
표준 Yes, No또는 Cancel유형 질문입니다.
일반적인 bash 프롬프트에서 어떻게 이것을 수행합니까?


11
참고 : 프롬프트에 대한 규칙은 [yn]옵션 을 제시하면 대문자로 된 옵션이 기본값입니다 (예 : [Yn]기본값은 "yes", [yN]기본값은 "no"). 참조 ux.stackexchange.com/a/40445/43532
Tyzoid

read
ZSH

답변:


1617

쉘 프롬프트에서 사용자 입력을 얻는 가장 단순하고 가장 널리 사용되는 방법은 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

또 다른 방법으로, 지적 으로 스티븐 Huwig은 , 배쉬의입니다 select명령. 다음은 동일한 예입니다 select.

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

분명히 다른 통신 문자열은 여기에서 번역되지 않은 채로 남아 있습니다 (설치, 답변). 더 완전한 완성 된 번역으로 해결해야하지만 부분 번역조차도 많은 경우에 도움이 될 것입니다.

마지막으로 F. Hauri훌륭한 답변 을 확인하십시오 .


30
OS X 레오파드에 배쉬를 사용하여, 나는 변경 exit하는 break내가 '더'를 선택하면 탭을 폐쇄하지 막기 위해.
Trey Piepmeier 2009

1
예 또는 아니요보다 긴 옵션에서 어떻게 작동합니까? case 절에서 다음과 같이 작성하십시오. 프로그램 설치 및 나중에 아무것도하지 마십시오) make install; 단절;
Shawn

1
@Shawn read 를 사용 하면 bash 쉘의 switch 문에서 지원하는 glob 또는 regex 패턴을 사용할 수 있습니다. 일치하는 것을 찾을 때까지 패턴에 입력 한 텍스트와 일치합니다. select를 사용하면 선택 사항 목록이 명령에 제공되고 사용자에게 선택 사항이 표시됩니다. 목록의 항목을 원하는만큼 길거나 짧게 만들 수 있습니다. 포괄적 인 사용법 정보는 해당 매뉴얼 페이지를 확인하는 것이 좋습니다.
Myrddin Emrys

3
왜 거기는 breakselect경우에는 루프가 없다?
Jayen

1
@akostadinov 편집 기록을 더 읽어야합니다. 당신은 정확하고 틀 렸습니다. 저는 Jayan의 조언에 따라 버그를 소개했습니다. 버그는 나중에 수정되었지만 수정 내용이 너무 멀리 떨어져 있어도 변경 내용을 기억하지 못했습니다.
Myrddin Emrys

526

하나의 일반적인 질문에 대해 최소 5 개의 답변.

에 따라

  • 준수 : 일반 시스템이있는 열악한 시스템에서 작동 할 수 있음 환경
  • 특정 : 소위 bashisms 사용

그리고 원한다면

  • 간단한``인라인 ''질문 / 응답 (일반 솔루션)
  • 꽤 형식화 된 인터페이스 libgtk 또는 libqt를 사용하는 그래픽
  • 강력한 리드 라인 히스토리 기능 사용

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의 의견에 감사드립니다 : 위의 테스트를 더 휴대하기 쉽고 포크 하나를 피하는 테스트로 대체했습니다.)

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

전용 도구 사용

사용하여 구축 된 다양한 도구가 있습니다 libncurses, libgtk, libqt또는 다른 그래픽 라이브러리. 예를 들어, 사용 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. 배쉬 특정 솔루션

기본 인라인 방법

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에서 readcommand는 시간 초과 매개 변수를 승인하는데 이는 유용 할 수 있습니다.

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 장치USB 이동식 저장소 선택기 선택을 위해 whiptail 사용을 살펴보십시오 : 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의 역사 명령을 사용할 수있는 것보다, 디렉토리 Up, Down, Ctrl+ r등이 있습니다.


4
참고 stty제공하는 -g사용하기위한 옵션을 : old_stty=$(stty -g); stty raw -echo; …; stty "$old_stty". 이렇게하면 설정을 찾은 그대로 정확하게 복원 할 수 있으며, 설정과 같거나 같지 않을 수 있습니다 stty -sane.
Jonathan Leffler

3
read answer공백과 줄 바꿈 이전의 백 슬래시를 해석하고 의도하지 않은 백 슬래시를 제거합니다. SC2162에read -r answer 따라 대신 사용하십시오 .
vlfig

1
"readline의 기록 사용"방법은 OP의 질문에 매우 부적절합니다. 어쨌든 포함 시켜서 기쁩니다. 그 패턴으로 업데이트하려는 훨씬 더 복잡한 스크립트가 수십 개 있습니다!
Bruno Bronosky

5
당신은 사용할 수 있습니다 case(와일드 카드 조건보다는 떠들썩한 파티 문자열을 사용으로 잘 배쉬로 POSIX에 대한 case $answer in; [Yy]* ) echo Yes ;;),하지만 난 호의를 대신 조건문을 사용하여 선호 [ "$answer" != "${answer#[Yy]}" ]이상 당신 echo "$answer" | grep -iq ^y. 이식성이 뛰어나고 (GNU 이외의 일부 grep은 -q올바르게 구현 되지 않음) 시스템 호출이 없습니다. ${answer#[Yy]}매개 변수 확장을 사용하여을 (를) 제거 Y하거나 y시작 부분에서 시작하여 $answer둘 중 하나가있을 때 불평등을 유발합니다. 이것은 모든 POSIX 셸 (대시, ksh, bash, zsh, busybox 등)에서 작동합니다.
Adam Katz

1
@CarterPape 네, 농담이었습니다! 그러나이 결정적인 답변에서 많은 팁을 찾을 수 있습니다 (두 번째 포인트는 적어도 3 가지 다른 방법이 있습니다)! 그리고 ... 약 5 년 전부터, 당신은 나의 계산 방법에 대해 먼저 이야기하고 있습니다! ;-))
F. Hauri

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

25
DOS에서 '예, 아니요, 취소'대화 상자의 기능 중 일부만 구현하기 때문에 동의하지 않습니다. 구현하지 못한 부분은 입력 확인입니다 ... 유효한 답변을받을 때까지 반복됩니다.
Myrddin Emrys

1
(원래 질문 제목은 "Linux 쉘 스크립트에서 입력을 요청하는 방법은 무엇입니까?"
Pistos

8
그러나 원래 질문 설명은 변경되지 않으며 항상 예 / 아니요 / 취소 프롬프트에 대한 응답을 요청했습니다. 제목은 원래 제목보다 명확하게 업데이트되었지만 질문 설명은 항상 명확했습니다 (제 의견으로는).
Myrddin Emrys

165

내장 된 읽기 명령을 사용할 수 있습니다 . 이 -p옵션을 사용하여 사용자에게 질문을하십시오.

BASH4부터는 이제 -i답변을 제안하는 데 사용할 수 있습니다 .

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

(단 -e, 화살표 키로 줄을 편집 하려면 "readline"옵션을 사용해야합니다 )

"예 / 아니오"논리를 원하면 다음과 같이 할 수 있습니다.

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해당 파일이 열립니다.
Ken Sharp

-e두 번째 예에서 장점은 무엇입니까 (간단한 예 / 아니요)?
JBallin

-e -p대신에 사용해야 할 이유 는 -ep무엇입니까?
JBallin

1
-e플래그 / 옵션이 없으면 (구현에 따라) "y"를 입력하지 못하고 마음을 바꾸고 "n"(또는 그 문제에 대한 다른 것)으로 바꿀 수 있습니다. 명령을 문서화 할 때 옵션을 별도로 나열하면 가독성 / 명확성이 향상됩니다.
yPhil

109

Bash는 이 목적으로 선택 했습니다.

select result in Yes No Cancel
do
    echo $result
done

18
+1 정말 간단한 솔루션. 유일한 것 : 이것은 프롬프트와 프롬 트와 프롬 프를 ... exit안을 추가 할 때까지 :)
kaiser

5
(kaiser : 그것으로부터 벗어나려면, 단지 EOT :를 입력하십시오 Ctrl-D. 그러나 물론, 그것을 사용하는 실제 코드는 본문에서 휴식이나 출구가 필요합니다.)
Zorawar

12
이 though.You 1 2 또는 3을 입력하여 선택, y를 입력하거나 할 수 n은하지 않습니다
djjeck을

3
exit스크립트를 모두 break종료하고 현재있는 루프 만 종료합니다 ( 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 $yn하는 case "${yn:-Y}"기본값으로 예 갖도록
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
케이스 스위치가 하드 코딩 된 경우 'y'와 'n'을 매개 변수로 제공하여 inquire ()를 제공하는 이유는 무엇입니까? 그것은 단지 오용을 요구하는 것입니다. 그것들은 고정 된 매개 변수이며 변경할 수 없으므로 2 행의 에코는 다음과 같아야합니다. echo -n "$ 1 [Y / N]?" "변경할 수 없으므로 공급하지 않아야합니다.
Myrddin Emrys

1
@MyrddinEmrys 의견을 좀 더 자세히 설명해 주시겠습니까? 또는 기사 나 몇 가지 키워드에 대한 링크를 게시하여 스스로 조사 할 수 있습니다.
Mateusz Piotrowski 2016 년

4
@MateuszPiotrowski 댓글을 작성한 이후에 답변이 수정되어 개선되었습니다. 위의 '편집 된 12 월 23 일'링크를 클릭하면이 답변의 모든 과거 버전을 볼 수 있습니다. 2008 년에는 코드가 매우 달랐습니다.
Myrddin Emrys

27

당신이 원하는 :

  • 배쉬 내장 명령 (즉, 휴대용)
  • 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 => 한 문자를 기다립니다
  • $'\e[1;32m ... \e[0m '=> 녹색으로 인쇄
    (흰색 / 검정 배경 모두에서 읽을 수 있으므로 녹색이 양호 함)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash 정규식

Timeout => 기본 답변은 아니요

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

이 솔루션은 단일 문자를 읽고 예 응답에서 함수를 호출합니다.

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

2
@Jav 에코는 응답 후 줄 바꿈을 인쇄합니다. 그것이 없으면, 다음 줄에 인쇄 할 내용이 같은 줄에 응답 한 직후에 나타납니다. 를 제거하여 echo직접 확인하십시오.
Dennis

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 옵션은 암시 입력을 인쇄합니다.


yap, -e -ish (Bourne shell)에서는 작동하지 않지만 문제는 특별히 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_MESSAGES로케일 범주 의 항목을 사용하여 마녀는 입력과 일치하는 기성품 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는 처음 두 개 (yesexpr 및 noexpr) 만 지정합니다. 우분투 16에서 yesstr과 nostr은 비어 있습니다.

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

LC_MESSAGES

yesstrnostr로케일 키워드와 YESSTRNOSTR항목 langinfo는 이전에 일치하는 사용자 긍정과 부정 응답에 사용되었다. POSIX.1-2008에서,는 yesexpr, noexpr, YESEXPR, 및 NOEXPR확장 정규 표현식을 대체했다. 애플리케이션은 일반 로케일 기반 메시징 기능을 사용하여 원하는 응답 샘플이 포함 된 프롬프트 메시지를 발행해야합니다.

또는 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는 처음 두 개 (yesexpr 및 noexpr) 만 지정합니다. 우분투 16에서 yesstr과 nostr은 비어 있습니다.
Urhixidur

1
하지만 기다려! 더 나쁜 소식이 있습니다! bash case 문 표현식은 규칙적이지 않으며 파일 이름 표현식입니다. 따라서 Ubuntu 16의 yesexpr 및 noexpr (각각 "^ [yY]. *"및 "^ [nN]. *")는 포함 된 기간으로 인해 완전히 실패합니다. 정규식에서 ". *"는 "줄 바꿈이 아닌 문자, 0 번 이상"을 의미합니다. 그러나 사례 진술에서 문자 그대로 "."입니다. 뒤에 임의의 수의 문자가옵니다.
Urhixidur

1
마지막으로 함께 할 수있는 최선 yesexprnoexpr쉘 환경에서은, 배쉬의 특정 정규식 매칭에 사용입니다if [[ "$yn" =~ $yesexpr ]]; then echo $"Answered yes"; else echo $"Answered no"; fi
레아 그리

10

단일 키 누르기 만

더 길지만 재사용 가능한 모듈 식 접근 방식은 다음과 같습니다.

  • 0= yes와 1= no를 반환
  • 누르기 엔터 필요 없음-단일 문자 만
  • 를 눌러 enter기본 선택을 수락 할 수 있습니다
  • 선택을 강제하기 위해 기본 선택을 비활성화 할 수 있습니다
  • zsh와 모두 에 적용 bash됩니다.

Enter 키를 누를 때 기본값은 "no"입니다

(가) 있습니다 Ncapitalsed된다. 여기에서 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 키를 누를 때 기본값은 "yes"입니다

(가) 있습니다 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또는 거짓이 반환되었습니다. 이 하위 수준 기능을 사용하면 고유 한 [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으로,이 스크립트를 시험 할 때 내가 가지고, 위 Show dangerous command [y/N]? [y/n]?Show dangerous command [Y/n]? [y/n]?
일리아스 카림에게

@IliasKarim에게 감사드립니다. 지금 막 고쳤습니다.
Tom Hale

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 13:22의

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
$

(스크립트 내부)로 설정 REPLY됩니다 y.


4

대화 상자사용 하는 것이 좋습니다 ...

Linux Apprentice : 대화 상자를 사용하여 Bash Shell 스크립트 개선

대화 상자 명령을 사용하면 쉘 스크립트에서 창 상자를 사용하여 대화식으로 사용할 수 있습니다.

간단하고 사용하기 쉬우 며 gdialog라는 그놈 버전도 있습니다.이 버전에는 정확히 동일한 매개 변수를 사용하지만 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

이를 수행하는 간단한 방법 중 하나는 with xargs -p또는 gnu parallel --interactive입니다.

xargs의 동작은 마지막에 실행할 yesses를 수집하는 대신 다른 대화식 unix 명령과 같이 프롬프트 직후에 각 명령을 실행하기 때문에 조금 더 좋습니다. (원하는 것을 겪은 후에 Ctrl-C를 사용할 수 있습니다.)

예를 들어

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

나쁘지 xargs --interactive는 않지만 예 또는 아니오로 제한됩니다. 그것이 충분하면 충분할 수 있지만, 원래의 질문은 세 가지 가능한 결과를 가진 예를 보여주었습니다. 나는 그것이 스트리밍 가능하다는 것을 정말로 좋아합니다. 많은 일반적인 시나리오는 파이프 기능을 통해 이점을 얻을 수 있습니다.
Myrddin Emrys

내가 참조. 내 생각은 "취소"는 Ctrl-C를 통해 지원되는 모든 추가 실행을 단순히 중지하는 것을 의미하지만 취소 (또는 아니오)에서 더 복잡한 작업을 수행 해야하는 경우 충분하지 않습니다.
Joshua Goldberg

3

한 줄 명령의 친구로서 다음을 사용했습니다.

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=;

longform으로 작성하면 다음과 같이 작동합니다.

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

while 루프 후 프롬프트가 지워집니다. 프롬프트 변수가 나중에 초기화되기를 원하기 때문에 (문을 더 자주 사용하기 때문에). 쉘 스크립트에이 줄이 있으면 y | Y를 입력 한 경우에만 진행되고 n | N을 입력하면 종료되거나 다른 모든 것에 대한 입력을 반복하십시오.
ccDict

3

내가 사용했습니다 case그것에 대해 갈 수있는 좋은 방법을 사례 한 Statment이다 사용하여 이러한 시나리오에 문을 몇 번. 프로그램을 더 많이 제어하고 다른 많은 요구 사항을 충족시키기 위해 부울 조건을 사용하는 블록 while을 캡슐화 하는 루프를 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
트리플 리 '

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.