Linux kabuk betiğinde Evet / Hayır / İptal girdisini nasıl isteyebilirim?


1443

Bir kabuk komut dosyasında girişi duraklatmak ve seçimler için kullanıcıdan sormak istiyorum.
Standart Yes, Noya Canceltip bir soru.
Bunu tipik bir bash isteminde nasıl başarabilirim?


11
Not: komut istemleri için kural, bir [yn]seçeneği sunarsanız, büyük harfle yazılmış olanın varsayılan olduğu, yani [Yn]varsayılan olarak "evet" ve [yN]varsayılan olarak "hayır" olacağı şekildedir . Bkz. Ux.stackexchange.com/a/40445/43532
Tyzoid

Buraya read
ZSH'den

Yanıtlar:


1617

Kabuk isteminde kullanıcı girişi almanın en basit ve en yaygın yöntemi readkomuttur. Kullanımını göstermenin en iyi yolu basit bir gösteri:

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

Diğer bir yöntem, işaret tarafından Steven Huwig , Bash'in olan selectkomut. İşte aynı örneği kullanarak 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

İle selectsize girdileri gerekmez - bu mevcut seçenekler görüntüler ve seçtiğiniz karşılık gelen bir sayı yazın. Ayrıca otomatik olarak döngüye girer, bu nedenle while truegeçersiz giriş verirse yeniden denemek için bir döngüye gerek yoktur .

Ayrıca Léa Gris , istek dilini yanıtında agnostik hale getirmenin bir yolunu gösterdi . İlk örneğimi birden çok dile daha iyi hizmet verecek şekilde uyarlamak şöyle görünebilir:

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

Açıkçası, diğer iletişim dizeleri burada daha tam olarak tamamlanmış bir çeviride ele alınması gereken tercüme edilmemiş halde kalır (Kur, Cevapla), ancak çoğu durumda kısmi bir çeviri bile yardımcı olacaktır.

Son olarak, kontrol edin mükemmel cevabı ile F. Hauri .


30
OS X Leopard Bash kullanarak, değiştirilen exitiçin breakben 'hayır' seçildiğinde sekme kapatmasını tutmak.
Trey Piepmeier

1
Bu Evet veya Hayır'dan daha uzun seçeneklerle nasıl çalışır? Dava maddesine benzer bir şey yazıyorsunuz: Programı kurun ve daha sonra hiçbir şey yapmayın) install; break;
Shawn

1
Kullanımı @Shawn okumak sen, elbette, bash kabuk switch ifadesi tarafından desteklenen herhangi bir topak veya düzenli ifade deseni kullanabilirsiniz. Bir eşleşme bulana kadar desenlerinize yazılan metinle eşleşir. Kullanılması seçme , seçimler liste komutuna verilen ve kullanıcıya görüntüler edilir. Listedeki öğelerin istediğiniz kadar uzun veya kısa olmasını sağlayabilirsiniz. Kapsamlı kullanım bilgileri için man sayfalarını kontrol etmenizi tavsiye ederim.
Myrddin Emrys

3
neden orada bir olduğu breakiçinde selecteğer hiçbir döngü var mı?
Jayen

1
@akostadinov Gerçekten düzenleme geçmişimi daha fazla okumalıyım. Haklısın, yanılıyorum ve Jayan'ın tavsiyesi üzerine bir hata getirdim, hah. Hata daha sonra düzeltildi, ancak düzenlemeler o kadar uzaktı ki değiştirmeyi bile hatırlamıyordum.
Myrddin Emrys

526

Bir genel soru için en az beş cevap.

Bağlı olarak

  • uyumlu: jenerik olan zayıf sistemler üzerinde çalışabilir ortamları
  • özgü: bashisms adı verilen kullanma

ve eğer istersen

  • basit `` sıralı '' soru / cevap (genel çözümler)
  • gibi oldukça biçimlendirilmiş arayüzler veya libgtk veya libqt kullanarak daha fazla grafik ...
  • güçlü readline geçmişi özelliğini kullan

1. POSIX genel çözümleri

readKomutu ve ardından şunu kullanabilirsiniz 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'ın yorumu sayesinde : Yukarıdaki testi daha taşınabilir olan ve bir çataldan kaçınan bir testle değiştirdi :)

POSIX, ancak tek tuş özelliği

Ancak kullanıcının vurmasını istemiyorsanız Returnşunları yazabilirsiniz:

( Düzenlendi: @JonathanLeffler'in haklı olarak önerdiği gibi, stty'nin yapılandırmasını kaydetmek onları aklı başında tutmaya zorlamaktan daha iyi olabilir .)

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

Not: Bu,, , , ve !

Aynı, ancak açıkça yveya 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

Özel araçlar kullanma

Kullanılarak geliştirilmiş birçok araç vardır libncurses, libgtk, libqtveya diğer grafik kütüphaneler. Örneğin, şunu kullanarak whiptail:

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

Sisteminize bağlı olarak, whiptailbaşka bir benzer araçla değiştirmeniz gerekebilir :

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

Burada 20iletişim kutusunun satır sayısı cinsinden yüksekliği ve iletişim kutusunun 60genişliği. Bu araçların hepsi aynı sözdizimine sahiptir.

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

2. Bash'e özel çözümler

Temel hat yöntemi

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

Kullanmayı tercih ederim, caseböylece yes | ja | si | ouigerekirse test edebilirim ...

doğrultusunda ile tek bir anahtar özelliği

Bash altında, readkomut için amaçlanan girişin uzunluğunu belirleyebiliriz :

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

Bash altında, readkomut faydalı olabilecek bir zaman aşımı parametresini kabul eder .

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. Özel araçlar için bazı hileler

Basit yes - noamaçların ötesinde daha sofistike iletişim kutuları :

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

İlerleme çubuğu:

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
) 

Küçük demo:

#!/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

Daha fazla örnek? Göz at USB cihazını seçmek için whiptail kullanma ve USB çıkarılabilir depolama seçici: USBKeyChooser

5. Readline'ın geçmişini kullanma

Misal:

#!/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

Bu bir dosya oluşturacaktır .myscript.historysizin de $HOMEsizin gibi, Readline'ın geçmişi komutlarını kullanabilirsiniz daha dizinde Up, Down, Ctrl+ rve diğerleri.


4
Not sttysağlayan -gkullanımı için seçenek: old_stty=$(stty -g); stty raw -echo; …; stty "$old_stty". Bu, ayarı tam olarak bulundukları gibi geri yükler; bu, aynı olabilir veya olmayabilir stty -sane.
Jonathan Leffler

3
read answerboşluklardan ve satır beslemelerinden önce ters eğik çizgileri yorumlar ve aksi takdirde nadiren amaçlanan şeritleri yorumlar. Kullanım read -r answerbaşına yerine SC2162 .
vlfig

1
"Readline'ın geçmişini kullanma" yöntemi OP sorusu için son derece uygunsuz. Yine de dahil ettiğiniz için mutluyum. Bu kalıpla güncellemeyi planladığım çok daha karmaşık komut dosyalarına sahibim!
Bruno Bronosky

5
Sen kullanabilirsiniz case(joker koşulu yerine bash alt dize kullanın: yanı bash olarak POSIX case $answer in; [Yy]* ) echo Yes ;;), ama lehine yerine koşullu deyimi kullanmayı tercih [ "$answer" != "${answer#[Yy]}" ]üzerinde senin echo "$answer" | grep -iq ^y. Daha taşınabilir (bazı GNU harici açılımlar -qdoğru şekilde uygulanmıyor ) ve sistem çağrısı yok. ${answer#[Yy]}parametresini genişletmek için Yveya ybaşlangıcından itibaren parametre genişlemesi kullanır ve bu $answerdurum her ikisinde de eşitsizliğe neden olur. Bu, herhangi bir POSIX kabuğunda (tire, ksh, bash, zsh, busybox vb.) Çalışır.
Adam Katz

1
@CarterPape Evet, bu bir şakaydı! Ancak bu ayrıntılı cevapta çok fazla ipucu bulabilirsiniz (ikinci nokta en az 3 farklı yöntem sunar)! Ve ... yaklaşık 5 yıl sonra, ilk olarak sayma yöntemimi anlatacaksınız! ;-))
F. Hauri

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

25
Kabul etmiyorum, çünkü DOS'taki 'Evet, Hayır, İptal' iletişim kutusunun işlevselliğinin yalnızca bir kısmını uyguluyor. Uygulayamadığı kısım, geçerli bir yanıt alınana kadar giriş kontrolü ... döngüsüdür.
Myrddin Emrys

1
(Orijinal soru başlığı "Linux kabuk betiğinde nasıl giriş
isteyebilirim

8
Ancak orijinal soru açıklaması değişmez ve her zaman Evet / Hayır / İptal istemine yanıt istenir. Başlık orijinal kitabımdan daha net olacak şekilde güncellendi, ancak soru açıklaması her zaman açıktı (bence).
Myrddin Emrys

165

Yerleşik okuma komutunu kullanabilirsiniz; -pKullanıcıya bir soru sormak için bu seçeneği kullanın .

BASH4'ten bu yana, şimdi -ibir cevap önermek için kullanabilirsiniz :

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

(Ancak -eok tuşlarıyla satır düzenlemeye izin vermek için "readline" seçeneğini kullanmayı unutmayın )

Bir "evet / hayır" mantığı istiyorsanız, bunun gibi bir şey yapabilirsiniz:

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

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

6
Unutulmamalıdır FILEPATHseçtiğiniz değişken adıdır ve istemi komuta cevap ayarlanır. Böylece vlc "$FILEPATH", örneğin, eğer çalıştırırsanız , vlco dosyayı açarsınız.
Ken Sharp

-eİkinci örnekte faydası nedir (basit evet / hayır)?
JBallin

-e -pBunun yerine kullanmak için herhangi bir neden var -epmı?
JBallin

1
-eBayrak / seçenek olmadan (uygulamaya bağlı olarak) "y" yazamaz ve ardından fikrinizi değiştirip yerine "n" (veya bu konu için başka bir şey) koyamazsınız; Bir komutu belgelerken, diğer nedenlerin yanı sıra, okunabilirlik / netlik için seçenekleri ayrı olarak listelemek daha iyidir.
yPhil

109

Bash bu amaç için seçim yaptı .

select result in Yes No Cancel
do
    echo $result
done

18
+1 Çok basit bir çözüm. Tek şey: Bu bir istem ekleyecek ve teşvik edecek ve isteyeceksiniz ... siz bir exitiç ekleyene kadar :)
kaiser

5
(kaiser: Bundan kurtulmak için sadece EOT'a girin: Ctrl-DAma elbette, onu kullanan gerçek kodun bir mola veya vücutta bir çıkışa ihtiyacı olacaktır.)
Zorawar

12
Bu, y veya n girmenize izin vermeyecektir. 1 2 veya 3 girerek seçebilirsiniz
djjeck

3
exitkomut dosyasından hep birlikte breakçıkacak, yalnızca bulunduğunuz döngüden çıkacak (bir whileveya casedöngüde
iseniz

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

İşte bir araya getirdiğim bir şey:

#!/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

Ben bir acemiyim, bu yüzden bunu bir tane tuzla al, ama işe yarıyor gibi görünüyor.


9
Eğer değiştirirseniz case $yn iniçin case ${yn:-$2} ino zaman varsayılan değer olarak ikinci argümanı kullanabilir, Y ve N
jchook

1
veya değiştirmek case $yniçin case "${yn:-Y}"varsayılan olarak evet var
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
Kodun biçimlendirmesini korumak için her satırın başına dört boşluk koyun.
Jouni K. Seppänen

10
Neden vaka anahtarlarının sabit kodlanıp kodlanmadığını sorgulamak () için parametreler olarak 'y' ve 'n' sağlıyoruz? Bu sadece yanlış kullanım istiyor. Sabit parametrelerdir, değiştirilemezler, bu nedenle 2. satırdaki yankı şöyle olmalıdır: echo -n "$ 1 [E / H]?"
Myrddin Emrys

1
@MyrddinEmrys Yorumunuzu ayrıntılı olarak açıklar mısınız? Ya da bir makaleye veya birkaç anahtar kelimeye bağlantı yayınlayın, böylece kendi başıma araştırma yapabilirim.
Mateusz Piotrowski

4
@MateuszPiotrowski Yorumumu yaptığımdan beri cevap düzenlendi ve geliştirildi. Bu cevabın tüm geçmiş sürümlerini görüntülemek için yukarıdaki '23 Aralık' düzenlendi 'bağlantısını tıklayabilirsiniz. 2008 yılında, kod oldukça farklıydı.
Myrddin Emrys

27

İstediğiniz:

  • Bash yerleşik komutları (örn. Taşınabilir)
  • TTY'yi kontrol et
  • Varsayılan cevap
  • Zaman aşımı
  • Renkli soru

Pasaj

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

açıklamalar

  • [[ -t 0 ]] && read ...=> readTTY ise çağrı komutu
  • read -n 1 => Bir karakter bekle
  • $'\e[1;32m ... \e[0m '=> Yeşil renkte yazdır
    (yeşil, her iki beyaz / siyah arka planda okunabileceğinden iyi)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash regex

Zaman aşımı => Varsayılan cevap Hayır

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

Bunu en az sayıda hatla elde etmenin en kolay yolu aşağıdaki gibidir:

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

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

Bu ifsadece bir örnektir: bu değişkenin nasıl işleneceği size bağlıdır.


20

readKomutunu kullanın :

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

ve sonra ihtiyacınız olan diğer tüm şeyler


17

Bu çözüm tek bir karakteri okur ve evet yanıtı için bir işlev çağırır.

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

2
@ Eko yankı yanıtınızdan sonra yeni bir satır yazdırır. Onsuz, yazdırılacak bir sonraki şey, aynı hatta verdiğiniz yanıttan hemen sonra görünecektir. echoKendiniz görmek için kaldırmayı deneyin .
Dennis

13

Güzel bir ncurses benzeri giriş kutusu almak için aşağıdaki gibi komut iletişim kutusunu kullanın :

#!/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

İletişim paketi varsayılan olarak en azından SUSE Linux ile kurulur. Görünüşe göre: "dialog" komutu iş başında


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

Bu -eseçenek kullanıcının ok tuşlarını kullanarak girişi düzenlemesini sağlar.

Bir öneriyi giriş olarak kullanmak istiyorsanız:

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

-i seçeneği müstehcen bir girdi yazdırır.


yap, -e -ish (Bourne shell) 'de çalışma, ama soru özellikle bash olarak etiketlendi ..
Jahid

12

Varsayılanı REPLYbir a read, küçük harfe dönüştür ve ifadeli bir değişken kümesiyle karşılaştırabilirsiniz.
Betik ayrıca 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 kabuğunda yerel ayarlı bir "Evet / Hayır seçimi" işlemek mümkündür; LC_MESSAGESyerel ayar kategorisinin girişlerini kullanarak , cadı bir girdiyle eşleşmek için hazır RegEx kalıpları ve yerelleştirilmiş Evet Hayır için dizeler sağlar.

#!/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

DÜZENLEME: As @Urhixidur belirtilen onun comment :

Ne yazık ki, POSIX yalnızca ilk ikisini belirtir (yesexpr ve noexpr). Ubuntu 16'da yesstr ve nostr boştur.

Bkz. Https://www.ee.ryerson.ca/~courses/ele709/susv4/xrat/V4_xbd_chap07.html#tag_21_07_03_06

LC_MESSAGES

yesstrVe nostryerel anahtar kelimeleri ve YESSTRve NOSTRöğeleri langinfo eskiden maç kullanıcı olumlu ve olumsuz tepkileri için kullanıldı. POSIX.1-2008, içinde yesexpr, noexpr, YESEXPRve NOEXPRgenişletilmiş düzenli ifadeler bunların yerini almış. Uygulamalar, istenen yerel yanıt örneklerini içeren mesajlar iletmek için yerel ayar tabanlı mesajlaşma olanaklarını kullanmalıdır.

Alternatif olarak Bash yolunu yerel ayarlar kullanarak:

#!/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

Yanıt, yerel ortamın sağladığı normal ifadeler kullanılarak eşleştirilir.

Kalan iletileri çevirmek için, bash --dump-po-strings scriptnameyerelleştirmeye yönelik po dizelerini çıktılamak üzere kullanın :

#: 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 ""

Bir dil agnostik seçeneğinin eklenmesini seviyorum. Aferin.
Myrddin

1
Maalesef, POSIX yalnızca ilk ikisini belirtir (yesexpr ve noexpr). Ubuntu 16'da yesstr ve nostr boştur.
Urhixidur

1
Fakat bekle! Daha kötü haberler var! Bash case deyimi ifadeleri düzenli değildir, dosya adı ifadeleridir. Dolayısıyla, Ubuntu 16'nın yesexpr ve noexpr ("^ [yY]. *" Ve "^ [nN]. *") Gömülü dönem nedeniyle tamamen başarısız olacaktır. Normal ifadede ". *" "Sıfır veya daha fazla kez yeni satır olmayan herhangi bir karakter" anlamına gelir. Ancak bir vaka ifadesinde, bu gerçek bir "." ardından herhangi bir sayıda karakter.
Urhixidur

1
Son olarak, bir kabuk ortamında yesexprve noexprbir kabuk ortamında yapılabilecek en iyi şey, Bash'in özel RegEx eşleşmesinde kullanmaktırif [[ "$yn" =~ $yesexpr ]]; then echo $"Answered yes"; else echo $"Answered no"; fi
Léa Gris

10

Yalnızca tek tuşa basma

İşte daha uzun ama yeniden kullanılabilir ve modüler bir yaklaşım:

  • 0= Evet ve 1= hayır döndürür
  • Enter tuşuna basmaya gerek yok - sadece tek bir karakter
  • enterVarsayılan seçimi kabul etmek için tuşuna basabilir
  • Seçimi zorlamak için varsayılan seçimi devre dışı bırakabilir
  • Hem için çalışır zshve bash.

Enter tuşuna basıldığında varsayılan olarak "hayır"

Büyük Nharfle yazıldığını unutmayın. Burada enter tuşuna basılarak varsayılan değer kabul edilir:

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

Ayrıca [y/N]?, otomatik olarak eklendiğini unutmayın . Varsayılan "hayır" kabul edilir, bu nedenle hiçbir şey yankılanmaz.

Geçerli bir yanıt verilene kadar yeniden yönlendirin:

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

Enter tuşuna basıldığında varsayılan olarak "evet"

Büyük Yharfle yazıldığını unutmayın:

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

Yukarıda, enter tuşuna bastım, bu yüzden komut koştu.

Varsayılan olarak açık değil enter- zorunlu yveyan

$ 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

Burada 1ya da yanlış döndürüldü. Bu alt düzey işlevle kendi [y/n]?isteminizi sağlamanız gerektiğini unutmayın .

kod

# 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
}

Bu senaryoyu test ettiğimde, yukarıdaki çıktı yerine, Show dangerous command [y/N]? [y/n]?veShow dangerous command [Y/n]? [y/n]?
Ilias Karim

Teşekkürler @IliasKarim, bunu şimdi düzelttim.
Tom Hale

9

Böyle eski bir gönderide yayınladığım için üzgünüm. Birkaç hafta önce benzer bir sorunla karşı karşıyaydım, benim durumumda, çevrimiçi bir yükleyici komut dosyasında da çalışan bir çözüme ihtiyacım vardı, örneğin:curl -Ss https://raw.github.com/_____/installer.sh | bash

Kullanılması read yesno < /dev/ttybenim için gayet iyi çalışıyor:

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

Umarım bu birine yardımcı olur.


Bunun önemli bir kısmı girdi doğrulamasıdır. Sanırım ilk örneğimi ttysizin için yaptığınız gibi girişi kabul edecek şekilde uyarlamak ve aynı zamanda kötü girdide döngüye girmek (tamponda birkaç karakter hayal etmek; yönteminiz kullanıcıyı her zaman hayır seçmeye zorlar ).
Myrddin Emrys

7

Kimsenin böyle basit kullanıcı girişi için çok satırlı yankı menüsünü gösteren bir cevap yayınlamadığını fark ettim, işte burada benim gitmem:

#!/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

Bu yöntem, birinin yararlı ve zaman kazandıran bulabileceği umuduyla yayınlandı.


4

Çoktan seçmeli versiyon:

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
}

Misal:

$ 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
$

Bu ayarlar REPLYiçin y(komut içinde).


4

İletişim kutusunu kullanmanızı öneririm ...

Linux Çırağı: İletişim Kutusunu Kullanarak Bash Kabuk Komut Dosyalarını Geliştirin

Diyalog komutu, kabuk komut dosyalarında pencere kutularının kullanımını daha etkileşimli hale getirmek için kullanılmasını sağlar.

basit ve kullanımı kolaydır, aynı parametreleri alan ancak X'te GUI stilini gösteren gdialog adlı bir gnome sürümü de vardır.


Sıradan okuyucu için, aşağıdaki iletişim komutunu kullanarak bir pasaja bakın: stackoverflow.com/a/22893526/363573
Stephan

4

@Mark ve @Myrddin'in cevaplarından esinlenerek bu işlevi evrensel bir istem için oluşturdum

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
}

şöyle kullanın:

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

4

daha genel:

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

Bunu yapmanın basit bir yolu xargs -pveya gnu'dur parallel --interactive.

Bunun için xargs'ın davranışını biraz daha beğendim çünkü her komutu komuttan sonra diğer etkileşimli unix komutları gibi hemen çalıştırıyor, sonunda çalıştırmak için yesses toplamak yerine. (İstediklerinize ulaştıktan sonra Ctrl-C tuşlarını kullanabilirsiniz.)

Örneğin,

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

Kötü değil, ancak xargs --interactiveevet veya hayır ile sınırlıdır. İhtiyacınız olan tek şey olduğu sürece bu yeterli olabilir, ancak orijinal sorum üç olası sonuçla bir örnek verdi. Gerçi akıcı olmasını gerçekten seviyorum; birçok yaygın senaryo, pipetleme yeteneğinden yararlanır.
Myrddin Emrys

Anlıyorum. Benim düşüncem, "iptal" in basitçe, bu Ctrl-C ile desteklediği tüm diğer yürütmeyi durdurmak anlamına geliyordu, ancak iptal (veya hayır) üzerinde daha karmaşık bir şey yapmanız gerekiyorsa, bu yeterli olmayacaktır.
Joshua Goldberg

3

Bir satır komutunun arkadaşı olarak aşağıdakileri kullandım:

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

Yazılı uzun biçimli, şöyle çalışır:

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

Bilgi istemi değişkeninin kullanımını açıklığa kavuşturabilir misiniz? Bir astardan sonra silinmiş gibi görünüyor, bu yüzden hattı bir şey yapmak için nasıl kullanıyorsunuz?
Myrddin Emrys

istemi while döngüsünden sonra silinir. Çünkü istem değişkeninin sonradan başlatılmasını istiyorum (çünkü ifadeyi daha sık kullanıyorum). Kabuk betiğinde bu satırın bulunması yalnızca y | Y yazılırsa devam eder ve n | N yazıldıysa çıkar veya yinelenen her şey için giriş ister.
ccDict

3

caseBöyle bir senaryoda ifadeyi birkaç kez kullandım, durum ifadesini kullanmak bunun için iyi bir yoldur. Boole koşul uygulayan bloğu whilekapsülleyen bir döngü, caseprogramın daha fazla kontrolünü sağlamak ve diğer birçok gereksinimi yerine getirmek için uygulanabilir. Tüm koşullar yerine getirildikten sonra, breakkontrolü programın ana kısmına geri döndürecek bir a kullanılabilir. Ayrıca, diğer koşulları karşılamak için, elbette kontrol yapılarına eşlik etmek için koşullu ifadeler eklenebilir: caseifade ve olası whiledöngü.

caseİsteğinizi yerine getirmek için ifade kullanma örneği

#! /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

Evet / Hayır / İptal

fonksiyon

#!/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
}

kullanım

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

Temiz kullanıcı girişi ile onaylayın

fonksiyon

#!/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
}

kullanım

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!'

Bu karmaşık ve kırılgan görünüyor. Peki yayn(){ read -s -n 1 -p '[y/n]'; test "$REPLY" = "y" ; } yn && echo success || echo failure
üçlü

2

Başkalarına yanıt olarak:

BASH4'te büyük / küçük harf belirtmeniz gerekmez, sadece küçük harf yapmak için ',,' kullanın. Ayrıca şiddetle okuma bloğunun içine kod koyarak sevmiyorum, sonucu almak ve IMO dışında okuma bloğu ile başa çıkmak. Ayrıca, IMO'dan çıkmak için bir 'q' ekleyin. Son olarak neden 'yes' yazmanız yeterlidir -n1 kullanın ve y tuşuna basın.

Örnek: kullanıcı sadece çıkmak için y / n ve q tuşlarına basabilir.

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

Bu tür senaryolarda çoğu zaman, kullanıcı "evet" girmeye devam edene kadar komut dosyasını yürütmeye devam etmeniz gerekir ve yalnızca kullanıcı "hayır" olarak girildiğinde durması gerekir. Aşağıdaki kod parçası bunu başarmanıza yardımcı olacaktır!

#!/bin/bash
input="yes"
while [ "$input" == "yes" ]
do
  echo "execute script functionality here..!!"
  echo "Do you want to continue (yes/no)?"
  read input
done
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.