Как мне запросить Да / Нет / Отменить ввод в сценарии оболочки Linux? - PullRequest
1267 голосов
/ 22 октября 2008

Я хочу приостановить ввод в сценарии оболочки и предложить пользователю выбор. Стандартный вопрос типа «Да, Нет или Отмена». Как мне сделать это в типичной командной строке?

Ответы [ 28 ]

1424 голосов
/ 22 октября 2008

Самым простым и широко доступным способом получения пользовательского ввода в командной строке является команда 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

Другим методом, на который указал Стивен Хьюиг, является команда Bash 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 вам не нужно очищать входные данные & ndash; он отображает доступные варианты, и вы вводите число, соответствующее вашему выбору. Он также выполняет циклы автоматически, поэтому нет необходимости повторять цикл while true, если они дают неверный ввод.

Также, пожалуйста, ознакомьтесь с превосходным ответом Ф. Хаури.

449 голосов
/ 10 января 2015

Как минимум пять ответов на один общий вопрос.

В зависимости от

  • совместимый: может работать на слабых системах с общей средами
  • специфично: использование так называемых bashisms

и если хотите

  • простой `` in line '' вопрос / ответ (общие решения)
  • довольно отформатированные интерфейсы, такие как или более графические с использованием libgtk или libqt ...
  • использовать мощные возможности истории readline

1. Общие решения POSIX

Вы можете использовать команду read, за которой следует if ... then ... else:

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

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

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

(Благодаря комментарию Адама Каца : заменен тест, приведенный выше, на более переносимый и исключающий одну развилку:)

POSIX, но функция с одним ключом

Но если вы не хотите, чтобы пользователь нажимал Return , вы можете написать:

( Отредактировано: Как справедливо предположил @JonathanLeffler, сохранение конфигурации stty может быть лучше, чем просто принудительно заставить их sane .)

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. Bash конкретные решения

Базовый в строке метод

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

Я предпочитаю использовать case, чтобы я мог даже проверить yes | ja | si | oui при необходимости ...

в строке с одной клавишей функция

В bash мы можем указать длину предполагаемого ввода для команды read:

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

В bash команда read принимает параметр timeout , который может быть полезен.

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

Некоторые хитрости для специализированных инструментов

Более сложные диалоговые окна, кроме простых 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

Еще образец? Взгляните на Использование whiptail для выбора USB-устройства и Селектор USB для съемной памяти: USBKeyChooser

5. Использование истории readline

* * Пример тысячу сто пятьдесят шесть: * * одна тысяча сто пятьдесят семь
#!/bin/bash

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

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

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

Это создаст файл .myscript.history в вашем каталоге $HOME, чем вы можете использовать команды истории readline, такие как Вверх , Вниз , Ctrl + r и др.

340 голосов
/ 22 октября 2008
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"
151 голосов
/ 13 мая 2011

Вы можете использовать встроенную команду read ; Используйте опцию -p, чтобы задать пользователю вопрос.

Начиная с BASH4, теперь вы можете использовать -i, чтобы предложить ответ, поэтому пользователю нужно только нажать return, чтобы ввести его:

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

(но не забудьте использовать опцию «readline» -e, чтобы разрешить редактирование строки с помощью клавиш со стрелками)

Если вам нужна логика «да / нет», вы можете сделать что-то вроде этого:

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

[[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/
102 голосов
/ 22 октября 2008

Bash имеет для этой цели .

select result in Yes No Cancel
do
    echo $result
done
54 голосов
/ 03 февраля 2011
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
32 голосов
/ 30 августа 2012

Вот что я собрал:

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

Я новичок, поэтому возьмите это с крошкой соли, но, похоже, это сработает.

28 голосов
/ 22 октября 2008
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?"

...
25 голосов
/ 18 декабря 2015

Вы хотите:

  • Встроенные команды Bash (т.е. переносимые)
  • Проверьте TTY
  • Ответ по умолчанию
  • Таймаут
  • Цветной вопрос

Отрывок

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

Пояснения

  • [[ -t 0 ]] && read ... => Команда вызова read, если TTY
  • read -n 1 => Ждать одного символа
  • $'\e[1;32m ... \e[0m ' => Печать зеленым цветом
    (зеленый хорошо, потому что читается как на белом, так и на черном фоне)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => регулярное выражение bash

Тайм-аут => Ответ по умолчанию: Нет

do_xxxx=y
[[ -t 0 ]] && {                   # Timeout 5 seconds (read -t 5)
read -t 5 -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx ||  # read 'fails' on timeout
do_xxxx=n ; }                     # Timeout => answer No
if [[ $do_xxxx =~ ^(y|Y|)$ ]]
then
    xxxx
fi
21 голосов
/ 04 марта 2016

Самый простой способ добиться этого с наименьшим количеством строк заключается в следующем:

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

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

if является лишь примером: вам решать, как обращаться с этой переменной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...