我想发出一个类似于这样的bash命令:
whiptail --title 'Select Database' --radiolist 'Select Database:' 10 80 2
1 production off
2 localhost on
Whiptail 对如何指定单选列表值相当讲究。如图所示,它们必须在各自的行上提供。这是一篇关于这个问题的好文章。
数据库列表在名为DBS
的变量中可用,ACTIVE_DB
是在鞭尾对话框中突出显示的单选列表项。
这是我目前为构建命令行所做的工作。这可能太复杂了。
DBS="production localhost"
ACTIVE_DB="localhost"
DB_COUNT="$( echo "$DBS" | wc -w )"
DB_LIST="$(
I=1
echo ""
for DB in $DBS; do
SELECTED="$( if [ "$DB" == "$ACTIVE_DB" ]; then echo " on"; else echo " off"; fi )"
SLASH="$( if (( $I < $DB_COUNT )); then echo \; fi )"
echo " $I $DB $SELECTED $SLASH"
echo ""
I=$(( I + 1 ))
done
)"
OPERATION="whiptail
--title "Select Database"
--radiolist
"Select Database:"
10 80 $DB_COUNT "${DB_LIST[@]}""
eval "${OPERATION}"
我离得很近。如您所见,扩展包含单引号,使事情变得混乱,并且在某些 EOL 中缺少反斜杠:
set -xv
++ whiptail --title 'Select Database' --radiolist 'Select Database:' 10 80 2 '
1 production off
2 localhost on '
该解决方案需要提供一种方法来以某种方式知道用户做了哪个选择,或者他们是否按下了 ESC。ESC 通常将返回代码设置为 255,因此这应该不难,但是在尝试检索用户选择的单选列表项的值时,此问题会变得非常混乱。
以下内容遵循 BashFAQ #50 中列出的最佳实践:
# note that lower-case variable names are reserved for application use by POSIX
# see https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
active_db="localhost"
dbs=( production localhost ) # using an array, not a string, means ${#dbs[@]} counts
# initialize an array with our explicit arguments
whiptail_args=(
--title "Select Database"
--radiolist "Select Database:"
10 80 "${#dbs[@]}" # note the use of ${#arrayname[@]} to get count of entries
)
i=0
for db in "${dbs[@]}"; do
whiptail_args+=( "$((++i))" "$db" )
if [[ $db = "$active_db" ]]; then # only RHS needs quoting in [[ ]]
whiptail_args+=( "on" )
else
whiptail_args+=( "off" )
fi
done
# collect both stdout and exit status
# to grok the file descriptor switch, see https://stackoverflow.com/a/1970254/14122
whiptail_out=$(whiptail "${whiptail_args[@]}" 3>&1 1>&2 2>&3); whiptail_retval=$?
# display what we collected
declare -p whiptail_out whiptail_retval
虽然我没有方便whiptail
进行测试,但上述代码运行的确切调用与以下内容完全相同:
whiptail --title "Select Database"
--radiolist "Select Database:" 10 80 2
1 production off
2 localhost on
。作为字符串,当eval
ED运行时,可以使用以下命令生成精确的命令:
printf '%q ' whiptail "${whiptail_args[@]}"; echo
使用数组。他们会让这变得容易十倍。让我们从小处着手,逐步向上。以下是数组形式的$DBS
和$DB_COUNT
:
DBS=(production localhost)
DB_COUNT=${#DBS[@]}
这里的优点是$DBS
实际上有两个条目,因此我们可以计算${#DBS[@]}
条目的数量,而不必掏出像wc
这样的外部命令。
所以现在让我们解决$DB_LIST
.您正在尝试在每次循环迭代中添加几个选项。让我们将其转换为数组语法,使用array+=(foo bar baz)
追加项目。
DB_LIST=()
I=1
for DB in "${DBS[@]}"; do
if [ "$DB" == "$ACTIVE_DB" ]; then SELECTED=on; else SELECTED=off; fi
DB_LIST+=("$I" "$DB" "$SELECTED")
I=$(( I + 1 ))
done
反斜杠和换行符由外壳解释,而不是鞭尾。没有必要将它们扔到数组中,所以我删除了整个$SLASH
变量。换行符无关紧要,所以再见echo ""
.
最后,让我们跑鞭尾。不再需要所有疯狂的引用和eval
。我们可以直接运行它,并在正确的位置扩展我们构建的阵列。
whiptail --title "Select Database" --radiolist "Select Database:" 10 80 "$DB_COUNT" "${DB_LIST[@]}"
还有一些清理工作可以做,但我认为这足以一天。