This came from a pair of sources; my work on portable shell, and the discovery that glibc's tzselect "requires ksh".
Originally, I thought "there's no way you could implement select in a shell which doesn't have it, because it's a control structure". And indeed, you can't. But you can implement a shell function such that while func_select var in args do done is equivalent (nearly) to select var in args do done I enclose func_select below. Weaknesses: 1. Newline after prompt. (Solving this portably seemed like too much work.) 2. "in args" is not optional; it can't implicitly use [EMAIL PROTECTED] So far as I can tell, this is basically solid. What'd I miss? func_select () { func_select_args=0 if expr "$1" : "^[_a-zA-Z][_a-zA-Z0-9]*$" > /dev/null; then func_select_var=$1 else echo >&2 "func_select: '$1' is not a valid variable name." return 1 fi shift 1 case $1 in in) shift 1;; *) echo >&2 "func_select: usage: func_select var in ... (you must provide arguments)"; return 1;; esac case $# in 0) echo >&2 "func_select: usage: func_select var in ..."; return 1;; esac for func_select_arg do func_select_args=`expr $func_select_args + 1` eval func_select_a_$func_select_args=\$func_select_arg done REPLY="" while : do if test -z "$REPLY"; then func_select_i=1 while test $func_select_i -le $func_select_args do eval echo "\"\$func_select_i) \$func_select_a_$func_select_i\"" func_select_i=`expr $func_select_i + 1` done fi echo "${PS3-#? }" if read REPLY; then if test -n "${REPLY}"; then if expr "$REPLY" : '^[1-9][0-9]*$' > /dev/null; then if test "$REPLY" -ge 1 && test "$REPLY" -le $func_select_args; then eval $func_select_var=\$func_select_a_$REPLY else eval $func_select_var= fi else eval $func_select_var= fi return 0 fi else eval $func_select_var= return 1 fi done }