Bash咒语,以编程方式定义whiptail无线电列表

时间:2019-05-01 00:02:51

标签: bash whiptail

我要发出类似于以下内容的bash命令:

whiptail --title 'Select Database' --radiolist 'Select Database:' 10 80 2 \
  1 production off \
  2 localhost  on

Whiptail非常特别地关于如何指定无线电列表值。如图所示,必须在各自的行上分别提供它们。 Here is a good article on this question

数据库列表位于名为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,这应该不难,但是,当尝试检索用户选择的单选列表项的值时,此问题会变得非常混乱。

2 个答案:

答案 0 :(得分:3)

以下遵循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被运行时,可以使用以下命令生成精确的命令:

printf '%q ' whiptail "${whiptail_args[@]}"; echo

答案 1 :(得分:1)

使用数组。他们会使此操作变得容易十倍。让我们从小处着手,逐步发展。这是$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[@]}"

还有更多清理工作可以完成,但我认为一天就足够了。