[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: tasksel



On Sat, Aug 31, 2019 at 07:52:23PM +0100, Paul Sutton wrote:
> #!/bin/bash
> OPTIONS="Update List Upgrade Autoremove Clean Quit"
> select opt in $OPTIONS; do

:-( :-(

Please don't abuse string variables to hold lists in bash.  Bash has
array variables.  Use an array to hold a list.

>         elif [ "$opt" = "Upgrade" ] ; then
>             echo "Upgrade packages"
>             apt upgrade -y
>         elif [ "$opt" = "Autoremove" ] ; then
>             echo "Autoremove packages"
>             apt autoremove

You might want to learn about the case command.

> I am now trying to create a checkbox option menu so that the user can
> choose which options are needed then when pressing ok these are executed
> in order.
> 
> 
> whiptail --title "Check list example" --checklist \
> "Choose user's permissions" 20 78 4 \
> "NET_OUTBOUND" "Allow connections to other hosts" ON \
> "NET_INBOUND" "Allow connections from other hosts" OFF \
> "LOCAL_MOUNT" "Allow mounting of local devices" OFF \
> "REMOTE_MOUNT" "Allow mounting of remote devices" OFF
> 
> I am just struggling to figure out how to :
> 
> take the above, if the user chooses say 1,2 and 4 then the commands associated with those options are executed.

OK, let's start from the beginning.  First, we have to figure out which
dialog/whiptail subcommand you're using and how it *works*, and then write
the script around that.

You're using whiptail --checklist, apparently, so let's experiment to find
out how that one works.

First I'll type out a sample command and run it and see what happens:

wooledg:~$ whiptail --checklist "Choose wisely" 20 78 4 "NET_OUT" "Allow out" ON "NET_IN" "Allow in" OFF "LOCAL_MT" "Local mount" OFF "REMOTE_MT" "Remote mount" OFF

After pressing Enter, and then selecting options 1, 2 and 4 as you
indicated, my terminal looks like this:

wooledg:~$ whiptail --checklist "Choose wisely" 20 78 4 "NET_OUT" "Allow out" ON "NET_IN" "Allow in" OFF "LOCAL_MT" "Local mount" OFF "REMOTE_MT" "Remote mount" OFF
"NET_OUT" "NET_IN" "REMOTE_MT"wooledg:~$ 

So, it wrote the string ...

"NET_OUT" "NET_IN" "REMOTE_MT"

... WITH the quotes (!!!!1one), to either stdout or stderr.  We need to
figure out which one it was.

So I'll add a redirection to the command, and try again:

wooledg:~$ whiptail --checklist "Choose wisely" 20 78 4 "NET_OUT" "Allow out" ON "NET_IN" "Allow in" OFF "LOCAL_MT" "Local mount" OFF "REMOTE_MT" "Remote mount" OFF 2>foo

And after that:

wooledg:~$ whiptail --checklist "Choose wisely" 20 78 4 "NET_OUT" "Allow out" ON "NET_IN" "Allow in" OFF "LOCAL_MT" "Local mount" OFF "REMOTE_MT" "Remote mount" OFF 2>foo
wooledg:~$ cat foo
"NET_OUT" "NET_IN" "REMOTE_MT"wooledg:~$ 

So, it wrote the string '"NET_OUT" "NET_IN" "REMOTE_MT"' to stderr, with
no trailing newline.  All right.  It's not ideal, but we can live with it.
Since you've chosen simple all-alphabetic strings as your selection keys,
we can simply strip out all those quote characters, and then split it into
words.

Here's your script.  It pretty much hits the limits of what bash can
comfortably do.  If you need this to be *any* fancier or prettier than
it is, you should rewrite it in a better language.  (I added a few more
comments than I would've used in a real script, because it's intended
as a teaching example.  It would be slightly shorter with some of the
comments and blank lines removed.)

#!/bin/bash

# Associative arrays to map our codename keys to their labels, to their
# default states in the checklist, and to their action commands.

# We need a non-associative array to hold the keys so that their order is
# preserved in at least one place.  The order of the keys in an associative
# array is not preserved.

keys=(NET_OUTBOUND NET_INBOUND LOCAL_MOUNT REMOTE_MOUNT)

declare -A label
label=(
  [NET_OUTBOUND]="Allow connections to other hosts"
  [NET_INBOUND]="Allow connections from other hosts"
  [LOCAL_MOUNT]="Allow mounting of local devices"
  [REMOTE_MOUNT]="Allow mounting of remote devices"
)

declare -A default
default=(
  [NET_OUTBOUND]=ON
  [NET_INBOUND]=OFF
  [LOCAL_MOUNT]=OFF
  [REMOTE_MOUNT]=OFF
)

declare -A action
action=(
  [NET_OUTBOUND]=net_outbound
  [NET_INBOUND]=net_inbound
  [LOCAL_MOUNT]=local_mount
  [REMOTE_MOUNT]=remote_mount
)

# Functions to be called for each selection.

net_outbound() {
  : your code here
  echo "for testing: net_outbound was chosen"
}

net_inbound() {
  : your code here
  echo "for testing: net_inbound was chosen"
}

local_mount() {
  : your code here
  echo "for testing: local_mount was chosen"
}

remote_mount() {
  : your code here
  echo "for testing: remote_mount was chosen"
}

# Now, the checklist menu.

# Build up the argument list dynamically from the keys and their associated
# labels and default states.

args=(
  --title "Check list example"
  --checklist "Choose user's permissions"
  20 78 4
)

for k in "${keys[@]}"; do
  args+=("$k" "${label[$k]}" "${default[$k]}")
done

result=$(whiptail "${args[@]}" 2>&1 1>/dev/tty) || exit

# Now strip those horribly stupid literal quote characters from the result.

result=${result//\"/}

# Now split it into a list of codename keys.

read -ra results <<< "$result"

# Now iterate over that list, and run the action assigned to each codename.

for k in "${results[@]}"; do
  if [[ ! ${action[$k]} ]]; then
    echo "internal error: unrecognized selection '$k'" >&2
    continue
  fi
  "${action[$k]}"
done


Reply to: