3

I have a select statement that shows a list of dynamic files ($list). I'd like to be able to input "1, 2, 3" and it will have file 1, file 2, and file 3 be selected. How do I modify this select (maybe even a different structure is needed) to allow multiple options to be selected?

select option in $list; do
        case $option in
            * )
                if [ "$option" ]; then
                    echo "Selected: " $option
                    break
                else
                    echo "Invalid input. Try again."
                fi;
        esac
    done
4
  • Do you just want to add cases 1), 2), 3)? Or do you need something more complicated? Commented Aug 11, 2012 at 5:43
  • It will be more complicated. $list contains all the files in a specific directory. Adding and removing files to the directory will change the case number. I rather not update the shell script each time. Commented Aug 11, 2012 at 5:46
  • 2
    Perhaps you should consider another plan of attack, either using dialog or a more complex language. Commented Aug 11, 2012 at 6:21
  • You could possibly make pseudo-checkboxes using select but I think that's it. You're probably better off with dialog as @IgnacioVazquez-Abrams pointed out. Commented Aug 11, 2012 at 9:10

2 Answers 2

8

This code doesn't use select , but does pretty much what you want-

#! /bin/bash
files=("file1" "file2" "file3" "file4" "Quit")

menuitems() {
    echo "Avaliable options:"
    for i in ${!files[@]}; do
        printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${files[i]}"
    done
    [[ "$msg" ]] && echo "$msg"; :
}

prompt="Enter an option (enter again to uncheck, press RETURN when done): "
while menuitems && read -rp "$prompt" num && [[ "$num" ]]; do
    [[ "$num" != *[![:digit:]]* ]] && (( num > 0 && num <= ${#files[@]} )) || {
        msg="Invalid option: $num"; continue
    }
    if [ $num == ${#files[@]} ];then
      exit
    fi
    ((num--)); msg="${files[num]} was ${choices[num]:+un-}selected"
    [[ "${choices[num]}" ]] && choices[num]="" || choices[num]="x"
done

printf "You selected"; msg=" nothing"
for i in ${!files[@]}; do
    [[ "${choices[i]}" ]] && { printf " %s" "${files[i]}"; msg=""; }
done
echo "$msg"

Demo-

$ ./test.sh
Avaliable options:
  1 ) file1
  2 ) file2
  3 ) file3
  4 ) file4
  5 ) Quit
Enter an option (enter again to uncheck, press RETURN when done): 1
Avaliable options:
  1x) file1
  2 ) file2
  3 ) file3
  4 ) file4
  5 ) Quit
file1 was selected
Enter an option (enter again to uncheck, press RETURN when done): 2
Avaliable options:
  1x) file1
  2x) file2
  3 ) file3
  4 ) file4
  5 ) Quit
file2 was selected
Enter an option (enter again to uncheck, press RETURN when done): 3
Avaliable options:
  1x) file1
  2x) file2
  3x) file3
  4 ) file4
  5 ) Quit
file3 was selected
Enter an option (enter again to uncheck, press RETURN when done): 1
Avaliable options:
  1 ) file1
  2x) file2
  3x) file3
  4 ) file4
  5 ) Quit
file1 was un-selected
Enter an option (enter again to uncheck, press RETURN when done): 
You selected file2 file3
Sign up to request clarification or add additional context in comments.

2 Comments

When you prompt for "Enter an option", I'm looking to be able to input: "1, 2, 3" and it output, "You selected file1, file2, file3." Your code only allows you to select one option at a time then prompt the user again. I'm looking for one prompt and to be able to input multiple options.
If u are looking for that specific reqmt then i think you are better off with dialog. The input you are suggesting "1, 2, 3" could be too much error prone, i.e. user could enter "123", "1,23", "111", "1 2 3" etc. etc. and the code will spend more time parsing through and validating that rather than doing actual work. You would probably end up creating a huge regular expression "trap" which would be difficult to read, understand, and maintain.
0

As select always fills the variable REPLY, you could loop through your $list afterwards. (The VAR in $list $VAR is only filled, with single selection.) e.g:

select ITEMS in $list; do
    case $ITEMS in
        * )
            break
    esac
done

# it $ITEMS is empty, but $REPLY not, we got multi selection
if [ -z "$ITEMS" ] && [ -n "$REPLY" ]; then
    # add some spaces to begin and end for simpler checking below, regarding multi digit REPLY entries
    REPLY_LIST=" $REPLY "
    CNT=1
    for ITEM in $list; do
        if [[ "$REPLY_LIST" =~ " $CNT " ]]; then
            ITEMS="$ITEMS $ITEM"
        fi
        CNT=$((CNT+1))
    done
elif [ -z "$ITEMS" ]; then
    echo nothing selected
    exit
fi
echo "Selected: $ITEMS ($REPLY)"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.