26

Usability testing of a shell script I wrote found that people had different expectations on how to answer a question that expected ‘yes’ as the answer. See variations in the below code example.

Surely there must be a better way that what I came up with? What is your readable and shorter form take on this?

read -p 'Answer this question with yes: ' answer
if [ "$answer" = 'Y'
  -o "$answer" = 'YES'
  -o "$answer" = 'Yes'
  -o "$answer" = 'y'
  -o "$answer" = 'yes'
  -o some-alternate-condition ]; then

  echo 'Surely this can be written better?'
fi
4
  • 2
    Voted to close. While I'm interested in the question, it's probably more of a programming question, so should be on SO.
    – phunehehe
    Commented May 22, 2011 at 15:50
  • A programming answer might be something like Bash: Check if array element exists
    – phunehehe
    Commented May 22, 2011 at 15:57
  • 3
    @phunehehe: I disagree with moving to SO, not only because we have a history of accepting shell programming, but also because it's not just a question of how to do it but also a question of what the spec should be. Commented May 22, 2011 at 16:00
  • 4
    I wrote a script much like that once. Then the program was translated to French and Simplified Chinese. I skimmed the French version and saw my prompt was translated to "Enter oui or non" but of course the code still wanted "Y*" or "N*" Commented May 22, 2011 at 21:38

5 Answers 5

49

The UNIX standard provides example code for this using the locale utility:

if printf "%s\n" "$response" | grep -Eq "$(locale yesexpr)"
then
    affirmative processing goes here
else
    non-affirmative processing goes here
fi

The value for 'yesexpr' in the POSIX locale (and on English locales on real systems) is "^[yY]". It is to be interpreted as an extended regular expression. See also noexpr.

4
  • 1
    Nice. But I seem to be an idiot. Where can I find a list of the arguments that locale knows about? The man page on my Mac OS 10.5 box doesn't seem to have them. Commented May 22, 2011 at 20:48
  • Nice. Could you please link back to the source?
    – Daniel
    Commented May 22, 2011 at 21:51
  • 3
    @dmckee you can do locale -k LC_MESSAGES (and for the other LC_whatever), or see here for ones defined by the standard. @Daniel what source other than the locale utility page that I did link?
    – Random832
    Commented May 22, 2011 at 22:20
  • 1
    Great answer, should definitely be the accepted one.
    – jlliagre
    Commented Sep 16, 2013 at 11:52
14

Using a case is somewhat equivalent but not perfect since statements like YE are accepted.

read -p 'Answer this question with yes: ' answer
case "${answer}" in
    [yY]|[yY][eE][sS])
        echo 'Surely this can be written better?' ;;
esac
1
  • 3
    I would say the user’s intent is pretty clear from ‘ye’.
    – Daniel
    Commented May 22, 2011 at 18:29
6

Staying in bash (or any other shell if you display the prompt independently):

case $answer in
  [Yy]*) echo Ok;;
  *) echo "Can't you read? I said to say yes.";;
esac

This accepts responses like yn as yes,  y (with an initial space) as no, and wlkjzuhfod as no, which may not be optimal but is consistent with typical shell prompts: that's how rm -i, find -ok and others do it.

This eschews the whole issue of internationalization: in other languages, you would need to translate expected responses. There's no standard shell method then; you can turn to dialog, but then your script will require it to be installed (it's available in many distributions but not always installed by default).

if dialog --yesno "Choose yes" 0 0; then …
3

I usually use a simple function:

Confirm() { read -sn 1 -p "$* [Y/N]? "; [[ ${REPLY:0:1} = [Yy] ]]; }

The function just returns 0 if Y or y is entered and 1 if anything else is entered. It can be used with if...fi:

if Confirm "Type y or n"; then
echo "You typed y"
else
echo "You typed n"
fi

Or just like this:

Confirm "Type y" && echo "You typed y"
1
  • Take off the -s to have a visual history of what what was typed.
    – Tom Hale
    Commented Sep 11, 2016 at 10:47
0

Just re-modified @faif ans

YesOrNo() {
        while :
        do
                read -p 'Do you want to Continue (yes/no?): ' answer
                case "${answer}" in
                    [yY]|[yY][eE][sS]) exit 0 ;;
                        [nN]|[nN][oO]) exit 1 ;;
                esac
        done
}


if $( YesOrNo ); then
        echo "Ans is yes.. Do something....."
else
        echo "Ans is No... skip.."
fi

Test

root@ubuntu:~# bash confirm.sh
Do you want to Continue (yes/no?):  # if Blank Enter then ask again
Do you want to Continue (yes/no?):
Do you want to Continue (yes/no?):
Do you want to Continue (yes/no?): no
Ans is No... skip..
root@ubuntu:~# bash confirm.sh
Do you want to Continue (yes/no?):
Do you want to Continue (yes/no?):
Do you want to Continue (yes/no?): ye
Do you want to Continue (yes/no?): yes
Ans is yes.. Do something.....

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .