Bagaimana saya meminta input Ya / Tidak / Batalkan dalam skrip shell Linux?


1444

Saya ingin menjeda input dalam skrip shell, dan meminta pengguna untuk memilih.
Pertanyaan standar Yes,, Noatau Canceljenis.
Bagaimana cara melakukannya dalam bash prompt yang khas?


11
Sama seperti catatan: konvensi untuk prompt adalah sedemikian rupa sehingga jika Anda menyajikan [yn]opsi, yang dikapitalisasi adalah default, yaitu [Yn]default ke "ya", dan [yN]default ke "tidak". Lihat ux.stackexchange.com/a/40445/43532
Tyzoid

Siapa pun yang datang ke sini dari ZSH, lihat jawaban ini untuk cara menggunakan readperintah untuk meminta
smac89

Jawaban:


1619

Metode paling sederhana dan paling banyak tersedia untuk mendapatkan input pengguna pada prompt shell adalah readperintahnya. Cara terbaik untuk menggambarkan penggunaannya adalah demonstrasi sederhana:

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

Metode lain, ditunjukkan oleh Steven Huwig , adalah selectperintah Bash . Berikut adalah contoh yang sama menggunakan 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

Dengan selectAnda tidak perlu membersihkan input - ini akan menampilkan pilihan yang tersedia, dan Anda mengetik angka yang sesuai dengan pilihan Anda. Itu juga loop otomatis, jadi tidak perlu while trueloop untuk mencoba lagi jika mereka memberikan input yang tidak valid.

Juga, Léa Gris menunjukkan cara untuk membuat bahasa permintaan agnostik dalam jawabannya . Mengadaptasi contoh pertama saya untuk melayani berbagai bahasa dengan lebih baik mungkin terlihat seperti ini:

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

Jelas string komunikasi lainnya tetap tidak diterjemahkan di sini (Instal, Jawab) yang perlu ditangani dalam terjemahan yang lebih lengkap, tetapi bahkan terjemahan parsial akan membantu dalam banyak kasus.

Akhirnya, silakan periksa jawaban luar biasa dari F. Hauri .


31
Menggunakan Bash di OS X Leopard, aku berubah exituntuk breakmenjaga dari penutupan tab ketika saya memilih 'tidak'.
Trey Piepmeier

1
Bagaimana cara kerjanya dengan opsi yang lebih lama dari Ya atau Tidak? Dalam klausa kasus, apakah Anda menulis sesuatu seperti: Instal program dan jangan lakukan apa-apa setelah itu) melakukan instal; istirahat;
Shawn

1
@Shawn Menggunakan read Anda dapat, tentu saja, menggunakan pola glob atau regex yang didukung oleh pernyataan switch bash shell. Itu hanya cocok dengan teks yang diketik ke pola Anda sampai menemukan kecocokan. Menggunakan pilih , daftar pilihan diberikan kepada perintah dan itu menampilkannya kepada pengguna. Anda dapat memiliki item dalam daftar selama atau sesingkat yang Anda suka. Saya merekomendasikan memeriksa halaman manual mereka untuk informasi penggunaan yang komprehensif.
Myrddin Emrys

3
mengapa ada breakdi selectjika tidak ada loop?
Jayen

1
@akostadinov Saya benar-benar harus membaca riwayat edit saya lebih lanjut. Anda benar, saya salah, dan saya memperkenalkan bug mengikuti saran Jayan, hah. Bug itu kemudian diperbaiki, tetapi pengeditannya begitu jauh sehingga saya bahkan tidak ingat mengubahnya.
Myrddin Emrys

527

Setidaknya lima jawaban untuk satu pertanyaan umum.

Bergantung kepada

  • compliant: dapat bekerja pada sistem yang buruk dengan generik lingkungan
  • spesifik: menggunakan bashisme yang disebut

dan jika kamu mau

  • pertanyaan / jawaban `` sejalan '' sederhana (solusi umum)
  • antarmuka yang cukup diformat, seperti atau lebih grafis menggunakan libgtk atau libqt ...
  • menggunakan kemampuan riwayat readline yang kuat

1. Solusi generik POSIX

Anda bisa menggunakan readperintah, diikuti oleh 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

(Terima kasih atas komentar Adam Katz : Mengganti tes di atas dengan yang lebih portabel dan menghindari satu garpu :)

POSIX, tetapi fitur kunci tunggal

Tetapi jika Anda tidak ingin pengguna harus menekan Return, Anda dapat menulis:

( Diedit: Seperti yang disarankan oleh @JonathanLeffler dengan benar, menyimpan konfigurasi stty bisa lebih baik daripada sekadar memaksa mereka untuk waras .)

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

Catatan: Ini diuji di bawah, , , dan !

Sama, tetapi menunggu secara eksplisit untuk yatau 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

Menggunakan alat khusus

Ada banyak alat yang dibangun menggunakan libncurses, libgtk, libqtatau perpustakaan grafis lainnya. Misalnya, menggunakan whiptail:

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

Bergantung pada sistem Anda, Anda mungkin perlu mengganti whiptaildengan alat serupa lainnya:

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

di mana 20tinggi kotak dialog dalam jumlah garis dan 60lebar kotak dialog. Semua alat ini memiliki sintaks yang hampir sama .

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

2. Bash solusi spesifik

Metode garis dasar

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

Saya lebih suka menggunakan casesehingga saya bahkan bisa menguji yes | ja | si | ouijika diperlukan ...

sejalan dengan fitur kunci tunggal

Di bawah bash, kita dapat menentukan panjang input yang dimaksudkan untuk readperintah:

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

Di bawah bash, readperintah menerima parameter batas waktu , yang bisa berguna.

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. Beberapa trik untuk alat khusus

Kotak dialog yang lebih canggih, di luar yes - notujuan sederhana :

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

Bilah kemajuan:

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
) 

Demo kecil:

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

Lebih banyak sampel? Lihat Menggunakan whiptail untuk memilih perangkat USB dan pemilih penyimpanan yang dapat dilepas USB: USBKeyChooser

5. Menggunakan riwayat readline

Contoh:

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

Ini akan membuat file .myscript.historydi Anda $HOMEdirektori, daripada Anda bisa menggunakan perintah sejarah readline ini, seperti Up, Down, Ctrl+ rdan lain-lain.


4
Perhatikan bahwa sttymenyediakan -gpilihan untuk digunakan: old_stty=$(stty -g); stty raw -echo; …; stty "$old_stty". Ini mengembalikan pengaturan persis seperti yang ditemukan, yang mungkin atau mungkin tidak sama dengan stty -sane.
Jonathan Leffler

3
read answerakan menginterpretasikan garis miring terbalik sebelum spasi dan umpan garis, dan jika tidak stripkan yang jarang dimaksudkan. Gunakan read -r answersebagai gantinya sesuai SC2162 .
vlfig

1
Metode "Menggunakan riwayat readline" sangat tidak pantas untuk pertanyaan OP. Saya senang Anda memasukkannya juga. Saya memiliki puluhan skrip yang lebih rumit yang ingin saya perbarui dengan pola itu!
Bruno Bronosky

5
Anda dapat menggunakan caseuntuk POSIX dan juga bash (gunakan kondisi wildcard daripada substring bash:) case $answer in; [Yy]* ) echo Yes ;;, tapi saya lebih suka menggunakan pernyataan kondisional, [ "$answer" != "${answer#[Yy]}" ]lebih memilih daripada echo "$answer" | grep -iq ^y. Ini lebih portabel (beberapa greps non-GNU tidak menerapkan -qdengan benar) dan tidak memiliki panggilan sistem. ${answer#[Yy]}menggunakan ekspansi parameter untuk menghapus Yatau ydari awal $answer, menyebabkan ketidaksetaraan saat ada. Ini berfungsi di semua shell POSIX (tanda hubung, ksh, bash, zsh, busybox, dll).
Adam Katz

1
@CarterPape Ya, ini hanya lelucon! Tetapi dalam jawaban yang sulit ini, Anda mungkin menemukan banyak tips (poin kedua menyajikan setidaknya 3 metode berbeda)! Dan ... dari sekitar 5 tahun sekarang, Anda yang pertama tahu tentang metode penghitungan saya! ;-))
F. Hauri

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

25
Saya tidak setuju, karena hanya mengimplementasikan sebagian dari fungsi dialog 'Ya, Tidak, Batalkan' di DOS. Bagian yang gagal diimplementasikan adalah pemeriksaan input ... pengulangan sampai jawaban yang valid diterima.
Myrddin Emrys

1
(Judul pertanyaan aslinya adalah "Bagaimana cara saya meminta input dalam skrip shell Linux?")
Pistos

8
Tetapi uraian pertanyaan awal tidak berubah, dan selalu meminta tanggapan terhadap permintaan Ya / Tidak / Batalkan. Judul telah diperbarui agar lebih jelas daripada yang asli saya, tetapi deskripsi pertanyaan selalu jelas (menurut saya).
Myrddin Emrys

165

Anda dapat menggunakan perintah baca bawaan; Menggunakan-p opsi untuk meminta pengguna dengan pertanyaan.

Sejak BASH4, sekarang Anda dapat menggunakan -iuntuk menyarankan jawaban:

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

(Tapi ingat untuk menggunakan opsi "readline" -euntuk memungkinkan pengeditan baris dengan tombol panah)

Jika Anda menginginkan logika "ya / tidak", Anda dapat melakukan sesuatu seperti ini:

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

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

6
Perlu dicatat bahwa itu FILEPATHadalah nama variabel yang Anda pilih, dan diatur dengan jawaban ke prompt perintah. Jadi jika Anda menjalankannya vlc "$FILEPATH", misalnya, vlcakan membuka file itu.
Ken Sharp

Apa manfaat dari -econtoh kedua (sederhana ya / tidak)?
JBallin

Ada alasan untuk menggunakannya, -e -pbukan -ep?
JBallin

1
Tanpa -etanda / opsi, Anda mungkin (tergantung pada implementasinya) tidak dapat mengetik "y", dan kemudian berubah pikiran dan menggantinya dengan "n" (atau apa pun dalam hal ini); Ketika mendokumentasikan perintah, daftar opsi secara terpisah lebih baik untuk keterbacaan / kejelasan, antara alasan lainnya.
yPhil

109

Bash telah memilih untuk tujuan ini.

select result in Yes No Cancel
do
    echo $result
done

18
+1 Solusi yang sangat sederhana. Satu-satunya hal: Ini akan meminta dan promt dan meminta ... sampai Anda menambahkan bagian exitdalam :)
kaiser

5
(kaiser: Untuk keluar darinya, cukup masukkan EOT:. Ctrl-DTapi tentu saja, kode nyata yang menggunakannya akan membutuhkan istirahat atau jalan keluar di tubuh.)
Zorawar

12
Namun, ini tidak akan memungkinkan Anda untuk memasukkan y atau n. Anda memilih dengan memasukkan 1 2 atau 3.
djjeck

3
exitakan keluar dari script bersama-sama, breakhanya akan keluar dari loop Anda (jika Anda berada di whileatau caseloop)
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

Inilah sesuatu yang saya kumpulkan:

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

Saya seorang pemula, jadi ambil ini dengan sebutir garam, tetapi sepertinya berhasil.


9
Jika Anda mengubah case $yn inke case ${yn:-$2} inmaka Anda dapat menggunakan argumen kedua sebagai nilai default, Y atau N.
jchook

1
atau ubah case $ynke case "${yn:-Y}"memiliki ya sebagai default
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
Letakkan empat spasi di awal setiap baris untuk mempertahankan pemformatan kode.
Jouni K. Seppänen

10
Mengapa kami menyediakan 'y' dan 'n' sebagai parameter untuk menanyakan () jika sakelar kasus di-hardcode? Itu hanya meminta penyalahgunaan. Mereka adalah parameter tetap, tidak dapat diubah, sehingga gema pada baris 2 harus berbunyi: echo -n "$ 1 [Y / T]?"
Myrddin Emrys

1
@MyrddinEmrys Bisakah Anda jelaskan komentar Anda? Atau memposting tautan ke artikel atau beberapa kata kunci sehingga saya bisa melakukan riset sendiri.
Mateusz Piotrowski

4
@MateuszPiotrowski Jawabannya telah diedit dan ditingkatkan sejak saya membuat komentar. Anda dapat mengklik tautan 'diedit 23 Des' di atas untuk melihat semua versi sebelumnya dari jawaban ini. Kembali pada tahun 2008, kodenya sangat berbeda.
Myrddin Emrys

27

Kamu ingin:

  • Perintah Bash builtin (yaitu portable)
  • Periksa TTY
  • Jawaban default
  • Waktu habis
  • Pertanyaan berwarna

Potongan

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

Penjelasan

  • [[ -t 0 ]] && read ...=> Panggil perintah readjika TTY
  • read -n 1 => Tunggu satu karakter
  • $'\e[1;32m ... \e[0m '=> Mencetak hijau
    (hijau baik-baik saja karena dapat dibaca pada latar belakang putih / hitam)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash regex

Timeout => Jawaban default adalah No

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

Cara termudah untuk mencapai ini dengan paling sedikit garis adalah sebagai berikut:

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

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

Ini ifhanyalah sebuah contoh: terserah Anda bagaimana menangani variabel ini.


20

Gunakan readperintah:

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

dan kemudian semua hal lain yang Anda butuhkan


17

Solusi ini membaca satu karakter dan memanggil fungsi pada respons ya.

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

2
@ Simpan gema mencetak baris baru setelah respons Anda. Tanpanya, hal berikutnya yang akan dicetak akan muncul segera setelah respons Anda pada baris yang sama. Coba hapus echountuk melihat sendiri.
Dennis

13

Untuk mendapatkan inputbox ncurses-like yang bagus gunakan dialog perintah seperti ini:

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

Paket dialog diinstal secara default setidaknya dengan SUSE Linux. Seperti: perintah "dialog" dalam aksi


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

Itu -e pilihan memungkinkan pengguna untuk mengedit masukan menggunakan tombol panah.

Jika Anda ingin menggunakan saran sebagai masukan:

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

-i opsi mencetak input sugestif.


ya, -e -ijangan bekerja di sh (Bourne shell), tetapi pertanyaannya ditandai bash khusus ..
Jahid

12

Anda dapat menggunakan default REPLYpada a read, konversikan ke huruf kecil dan bandingkan dengan serangkaian variabel dengan ekspresi.
Skrip juga mendukung 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

Dimungkinkan untuk menangani "Ya / Tidak pilihan" sadar-lokal di shell POSIX; dengan menggunakan entri dari LC_MESSAGESkategori lokal, penyihir menyediakan pola RegEx yang sudah jadi untuk mencocokkan input, dan string untuk dilokalisasi Ya Tidak.

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

EDIT: Seperti yang disebutkan oleh @Urhixidur dalam komentarnya :

Sayangnya, POSIX hanya menentukan dua yang pertama (yesexpr dan noexpr). Di Ubuntu 16, yesstr dan nostr kosong.

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

LC_MESSAGES

Kata kunci yesstrdan nostrlokal serta item YESSTRdan NOSTRlanginfo sebelumnya digunakan untuk mencocokkan respons afirmatif dan negatif pengguna. Dalam POSIX.1-2008, yang yesexpr, noexpr, YESEXPR, dan NOEXPRdiperpanjang ekspresi reguler telah menggantikan mereka. Aplikasi harus menggunakan fasilitas perpesanan berbasis lokal umum untuk mengeluarkan pesan prompt yang menyertakan sampel respons yang diinginkan.

Atau menggunakan lokal dengan cara 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

Jawabannya cocok dengan menggunakan regexps yang disediakan lingkungan lokal.

Untuk menerjemahkan pesan yang tersisa, gunakan bash --dump-po-strings scriptnameuntuk mengeluarkan string po untuk pelokalan:

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

Saya suka penambahan opsi agnostik bahasa. Sudah selesai dilakukan dengan baik.
Myrddin Emrys

1
Sayangnya, POSIX hanya menentukan dua yang pertama (yesexpr dan noexpr). Di Ubuntu 16, yesstr dan nostr kosong.
Urhixidur

1
Tapi tunggu! Ada berita buruk! Ekspresi pernyataan kasus bash tidak teratur, mereka adalah ekspresi nama file. Jadi Ubuntu 16's yesexpr dan noexpr ("^ [yY]. *" Dan "^ [nN]. *", Masing-masing) akan gagal total karena periode tertanam. Dalam ekspresi reguler, ". *" Berarti "karakter non-baris baru, nol atau lebih banyak kali". Tetapi dalam pernyataan kasus, ini literal "." diikuti oleh sejumlah karakter.
Urhixidur

1
Akhirnya yang terbaik yang dapat dilakukan dengan yesexprdan noexprdalam lingkungan shell, adalah menggunakannya dalam pencocokan RegEx spesifik Bashif [[ "$yn" =~ $yesexpr ]]; then echo $"Answered yes"; else echo $"Answered no"; fi
Léa Gris

10

Hanya menekan tombol saja

Inilah pendekatan yang lebih panjang, tetapi dapat digunakan kembali dan modular:

  • Pengembalian 0= ya dan 1= tidak
  • Tidak perlu menekan enter - hanya satu karakter
  • Dapat menekan enteruntuk menerima pilihan default
  • Dapat menonaktifkan pilihan default untuk memaksakan suatu pilihan
  • Bekerja untuk keduanya zshdan bash.

Standarnya menjadi "tidak" saat menekan enter

Perhatikan bahwa Nhuruf kapital. Sini masuk ditekan, menerima default:

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

Perhatikan juga, [y/N]?itu ditambahkan secara otomatis. Default "tidak" diterima, jadi tidak ada yang digaungkan.

Konfirmasikan kembali hingga respons yang valid diberikan:

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

Default ke "ya" saat menekan enter

Perhatikan bahwa Yhuruf kapital:

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

Di atas, saya hanya menekan enter, jadi perintahnya berlari.

Tidak ada default enter- memerlukan yataun

$ 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

Di sini, 1atau false dikembalikan. Perhatikan bahwa dengan fungsi level yang lebih rendah ini Anda harus menyediakan [y/n]?prompt Anda sendiri .

Kode

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

Ketika saya menguji skrip ini, bukannya outut di atas saya dapatkan, Show dangerous command [y/N]? [y/n]?danShow dangerous command [Y/n]? [y/n]?
Ilias Karim

Terima kasih @IliasKarim, saya memperbaikinya sekarang.
Tom Hale

9

Maaf karena memposting pada posting lama. Beberapa minggu yang lalu saya menghadapi masalah yang sama, dalam kasus saya, saya membutuhkan solusi yang juga berfungsi dalam skrip pemasang online, misalnya:curl -Ss https://raw.github.com/_____/installer.sh | bash

Menggunakan read yesno < /dev/ttyberfungsi dengan baik untuk saya:

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

Semoga ini bisa membantu seseorang.


Bagian penting dari ini adalah validasi input. Saya pikir mengadaptasi contoh pertama saya untuk menerima ttyinput seperti yang Anda lakukan akan melakukannya juga untuk Anda, dan juga mendapatkan perulangan pada input yang buruk (bayangkan beberapa karakter dalam buffer; metode Anda akan memaksa pengguna untuk selalu memilih tidak).
Myrddin Emrys

7

Saya perhatikan bahwa tidak ada yang memposting jawaban yang menunjukkan menu gema multi-line untuk input pengguna yang begitu sederhana jadi inilah yang saya lakukan:

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

Metode ini diposting dengan harapan bahwa seseorang dapat menemukannya berguna dan menghemat waktu.


4

Versi pilihan ganda:

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
}

Contoh:

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

Ini akan diatur REPLYke y(di dalam skrip).


4

Saya sarankan Anda menggunakan dialog ...

Linux Apprentice: Tingkatkan Skrip Bash Shell Menggunakan Dialog

Perintah dialog memungkinkan penggunaan kotak jendela dalam skrip shell untuk membuat penggunaannya lebih interaktif.

itu sederhana dan mudah digunakan, ada juga versi gnome yang disebut gdialog yang mengambil parameter yang sama persis, tetapi menunjukkannya gaya GUI di X.


Untuk pembaca biasa, lihat cuplikan menggunakan perintah dialog di sini: stackoverflow.com/a/22893526/363573
Stephan

4

Terinspirasi oleh jawaban @Mark dan @Myrddin saya membuat fungsi ini untuk prompt universal

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
}

gunakan seperti ini:

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

4

akan lebih umum:

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

Salah satu cara sederhana untuk melakukan ini adalah dengan xargs -patau gnu parallel --interactive.

Saya suka perilaku xargs sedikit lebih baik untuk ini karena ia mengeksekusi setiap perintah segera setelah prompt seperti perintah unix interaktif lainnya, daripada mengumpulkan yesses untuk dijalankan di akhir. (Anda dapat Ctrl-C setelah melewati yang Anda inginkan.)

misalnya,

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

Tidak buruk, tetapi xargs --interactiveterbatas pada ya atau tidak. Selama itu saja yang Anda butuhkan, itu sudah cukup, tetapi pertanyaan awal saya memberi contoh dengan tiga kemungkinan hasil. Saya sangat suka itu bisa di-streamable; banyak skenario umum akan mendapat manfaat dari kemampuannya untuk disalurkan.
Myrddin Emrys

Saya melihat. Pemikiran saya adalah bahwa "batal" dimaksudkan untuk menghentikan semua eksekusi lebih lanjut, yang didukung melalui Ctrl-C, tetapi jika Anda perlu melakukan sesuatu yang lebih kompleks pada membatalkan (atau tidak) ini tidak akan cukup.
Joshua Goldberg

3

Sebagai teman dari perintah satu baris saya menggunakan yang berikut:

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

Bentuk panjang tertulis, kerjanya seperti ini:

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

Bisakah Anda mengklarifikasi penggunaan variabel prompt? Sepertinya saya dihapus setelah liner, jadi bagaimana Anda menggunakan garis untuk melakukan sesuatu?
Myrddin Emrys

prompt dihapus setelah loop sementara. Karena saya ingin variabel prompt diinisialisasi setelah itu (karena saya lebih sering menggunakan pernyataan). Memiliki baris ini dalam skrip shell hanya akan dilanjutkan jika y | Y diketik dan keluar jika n | N diketik atau ulangi meminta input untuk semua yang lain.
ccDict

3

Saya telah menggunakan casepernyataan itu beberapa kali dalam skenario seperti itu, menggunakan statment kasus adalah cara yang baik untuk menyelesaikannya. Sebuah whileloop, yang meng-ecapsulate caseblok, yang memanfaatkan kondisi boolean dapat diimplementasikan untuk memegang kendali lebih besar terhadap program, dan memenuhi banyak persyaratan lainnya. Setelah semua persyaratan telah terpenuhi, breakdapat digunakan yang akan melewati kontrol kembali ke bagian utama dari program. Juga, untuk memenuhi kondisi lain, tentu saja pernyataan kondisional dapat ditambahkan untuk menyertai struktur kontrol: casepernyataan dan kemungkinan whileperulangan.

Contoh menggunakan casepernyataan untuk memenuhi permintaan Anda

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

Ya / Tidak / Batalkan

Fungsi

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

Pemakaian

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

Konfirmasikan dengan input pengguna yang bersih

Fungsi

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

Pemakaian

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

Ini tampaknya rumit dan rapuh. Bagaimana denganyn(){ read -s -n 1 -p '[y/n]'; test "$REPLY" = "y" ; } yn && echo success || echo failure
tripleee

2

Menanggapi orang lain:

Anda tidak perlu menentukan kasus di BASH4 cukup gunakan ',,' untuk membuat huruf kecil var. Saya juga sangat tidak suka menempatkan kode di dalam blok baca, dapatkan hasilnya dan mengatasinya di luar blok baca IMO. Juga sertakan 'q' untuk keluar dari IMO. Terakhir, mengapa mengetik 'ya' cukup gunakan -n1 dan tekan tombol y.

Contoh: pengguna dapat menekan y / n dan juga q untuk berhenti saja.

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

Seringkali dalam skenario seperti itu, Anda harus terus menjalankan skrip hingga pengguna terus memasukkan "ya" dan hanya perlu berhenti ketika pengguna memasukkan "tidak". Cuplikan di bawah ini akan membantu Anda mencapai ini!

#!/bin/bash
input="yes"
while [ "$input" == "yes" ]
do
  echo "execute script functionality here..!!"
  echo "Do you want to continue (yes/no)?"
  read input
done
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.