POSIX shell function for asking questions I've written a tiny function for asking questions intended for my POSIX shell scripts, where I often need user input.

The function takes 2+ arguments, where:


Is a string containing the question.
, 3., ... = arguments containing the right answers (case insensitive)


I also changed my style by a bit, no longer use ${var} instead of just $var.

The requirement was simple: Check for exactly the answers given. So, not matching yeahh if given answer is yeah.

I also included a maybe performance-wise quick test if the user answer is in the list of answers, so answering no will not make the script iterate through all the answers and just dies at that check.





#!/bin/sh
set -u
confirmation ()
# $1    = a string containing the question
# $2,.. = arguments containing the right answers (case insensitive)
{
    question=$1; shift
    correct_answers=$*
    correct_answers_combined=$( printf '%s' "$correct_answers" | sed 's/\( \)\{1,\}/\//g' )
    printf '%b' "$question\\nPlease answer [ $correct_answers_combined ] to confirm (Not &lt;Enter&gt;): "
    read -r user_answer

    # this part is optional in hope it would speed up the whole process
    printf '%s' "$correct_answers" | grep -i "$user_answer" &gt; /dev/null 2&gt;&amp;1 ||
        return 1

    # this part iterates through the list of correct answers
    # and compares each as the whole word (actually as the whole line) with the user answer
    for single_correct_answer in $correct_answers; do
        printf '%s' "$single_correct_answer" | grep -i -x "$user_answer" &gt; /dev/null 2&gt;&amp;1 &amp;&amp;
            return 0
    done

    # this might be omitted, needs verification, or testing
    return 1
}

# EXAMPLE usage, can be anything, DO NOT review this part please
if confirmation 'Is dog your favorite pet?' y yes yep yeah
then
    tput bold; tput setaf 2; echo 'TRUE: You just love dogs! :)'; tput sgr0
else
    tput bold; tput setaf 1; echo 'FALSE: Dog hater, discontinuing! :('; tput sgr0
    exit 1
fi
# do other stuff here in TRUE case
echo 'And here comes more fun...'
A couple of thoughts. There really isn't any need for correct_answers_combined. After shift, $* will hold the combined remaining arguments (answers) to your question. The additional printf, pipe, and call to sed are simply incurring additional overhead in the form of subshells and separate utility calls. You could do:

    correct_answers="$*"

    ## prompt
    printf '%b' "$question\\nPlease answer [ $correct_answers ] to confirm (Not &lt;Enter&gt;): "
    read -r user_answer


Since you do not want to accept [Enter] as an answer, a validation that user_answer is unset can provide the answer and return for your function in that case, e.g.

    ## validate answer provided
    [ -z "$user_answer" ] &amp;&amp; return 1


Your "optional... speed up the whole process", with a bit of rearranging can be used as a single call to provide a return to your function. Since you want to know whether the user_answer exists among the correct_answers you can simply return the result of grep -qi:

    # this part is can be the whole process
    printf '%s\n' $correct_answers | grep -qi "$user_answer" &gt; /dev/null 2&gt;&amp;1
        return $?
}


(note: using printf '%s\n' "$correct_answers" will separate each of the whitespace separated answers by newline which eliminates any possible combination of parts of adjacent answers returning true)

With those suggestions, your confirmation () function would reduce to:

confirmation ()
# $1    = a string containing the question
# $2,.. = arguments containing the right answers (case insensitive)
{
    question="$1"; shift
    correct_answers="$*"

    ## prompt
    printf '%b' "$question\\nPlease answer [ $correct_answers ] to confirm (Not &lt;Enter&gt;): "
    read -r user_answer

    ## validate answer provided
    [ -z "$user_answer" ] &amp;&amp; return 1

    # this part is can be the whole process
    printf '%s\n' "$correct_answers" | grep -qi "$user_answer" &gt; /dev/null 2&gt;&amp;1
        return $?
}


I haven't tested all corner cases, but for single word answers it should function as you intend. If you want the some delimiter between the answers inside [ ... ] then an additional command substitution can be used, but space separated options appear fine between the brackets.

Let me know if you have any questions over the changes.

Edit In Response to You Comments

As stated above, if you want delimiters between the possible correct answers, simply use a command substitution, e.g.

## prompt
printf '%b' "$question\\nPlease answer [ $(echo $correct_answers | tr ' ' /) ] to confirm (Not &lt;Enter&gt;): "
read -r user_answer


If you are concerned about the expansion of printf '%s\n', don't quote the $correct answers, 

# this part is can be the whole process
printf '%s\n' $correct_answers | grep -qi "$user_answer" &gt; /dev/null 2&gt;&amp;1
    return $?


Otherwise based on your stated question, it performs identical to the original saving at least half-a-dozen unnecessary subshells.