[ACCEPTED]-BASH scripts : whiptail file select-scripting

Accepted answer
Score: 11

Build an array of file names and menu select 19 tags:

i=0
s=65    # decimal ASCII "A" 
for f in *.tgz
do
    # convert to octal then ASCII character for selection tag
    files[i]=$(echo -en "\0$(( $s / 64 * 100 + $s % 64 / 8 * 10 + $s % 8 ))")
    files[i+1]="$f"    # save file name
    ((i+=2))
    ((s++))
done

A method like this will work even if 18 there are filenames with spaces. If the 17 number of files is large, you may have to 16 devise another tag strategy.

Using alpha 15 characters for the tags lets you press a 14 letter to jump to the item. Numeric tags 13 don't seem to do that. If you don't need 12 that behavior, then you can eliminate some 11 complexity.

Display the menu:

whiptail --backtitle "Welcome to SEUL" --title "Restore Files" \
    --menu "Please select the file to restore" 14 40 6 "${files[@]}"

If the exit 10 code is 255, the dialog was canceled.

if [[ $? == 255 ]]
then
    do cancel stuff
fi

To 9 catch the selection in a variable, use this 8 structure (substitute your whiptail command 7 for "whiptail-command"):

result=$(whiptail-command 2>&1 >/dev/tty)

Or

result=$(whiptail-command 3>&2 2>&1 1>&3-)

The variable $result will 6 contain a letter of the alphabet that corresponds 5 to a file in the array. Unfortunately, Bash 4 prior to version 4 doesn't support associative 3 arrays. You can calculate the index into 2 the array of the file from the letter like 1 this (notice the "extra" single quote):

((index = 2 * ( $( printf "%d" "'$result" ) - 65 ) + 1 ))

Example:

Welcome to SEUL
                ┌──────────┤ Restore Files ├───────────┐
                │ Please select the file to restore    │
                │                                      │
                │            A one.tgz      ↑          │
                │            B two.tgz      ▮          │
                │            C three.tgz    ▒          │
                │            D another.tgz  ▒          │
                │            E more.tgz     ▒          │
                │            F sp ac es.tgz ↓          │
                │                                      │
                │                                      │
                │       <Ok>           <Cancel>        │
                │                                      │
                └──────────────────────────────────────┘
Score: 3

Whiptail is a lightweight reimplementation 8 of the most popular features of dialog, using 7 the Newt library. I did a quick check, and many features 6 in Whiptail seem to behave like their counterparts 5 in dialog. So, a dialog tutorial should 4 get you started. You can find one here but 3 Google is your friend of course. On the 2 other hand, the extended example probably contains a lot 1 of inspiration for your problem.

Score: 1

This function is part of my function repository 1 for whiptail

# ----------------------------------------------------------------------
#  File selection dialog
#
#  Arguments
#     1  Dialog title
#     2  Source path to list files and directories
#     3  File mask (by default *)
#     4  "yes" to allow go back in the file system.
#
#  Returns
#     0  if a file was selected and loads the FILE_SELECTED variable 
#        with the selected file.
#     1  if the user cancels.
# ----------------------------------------------------------------------
function dr_file_select
{
    local TITLE=${1:-$MSG_INFO_TITLE}
    local LOCAL_PATH=${2:-$(pwd)}
    local FILE_MASK=${3:-"*"}
    local ALLOW_BACK=${4:-no}
    local FILES=()

    [ "$ALLOW_BACK" != "no" ] && FILES+=(".." "..")

    # First add folders
    for DIR in $(find $LOCAL_PATH -maxdepth 1 -mindepth 1 -type d -printf "%f " 2> /dev/null)
    do
        FILES+=($DIR "folder")
    done

    # Then add the files
    for FILE in $(find $LOCAL_PATH -maxdepth 1 -type f -name "$FILE_MASK" -printf "%f %s " 2> /dev/null)
    do
        FILES+=($FILE)
    done

    while true
    do
        FILE_SELECTED=$(whiptail --clear --backtitle "$BACK_TITLE" --title "$TITLE" --menu "$LOCAL_PATH" 38 80 30 ${FILES[@]} 3>&1 1>&2 2>&3)

        if [ -z "$FILE_SELECTED" ]; then
            return 1
        else
            if [ "$FILE_SELECTED" = ".." ] && [ "$ALLOW_BACK" != "no" ]; then
                return 0

            elif [ -d "$LOCAL_PATH/$FILE_SELECTED" ] ; then
                if dr_file_select "$TITLE" "$LOCAL_PATH/$FILE_SELECTED" "$FILE_MASK" "yes" ; then
                    if [ "$FILE_SELECTED" != ".." ]; then
                        return 0
                    fi
                else
                    return 1
                fi

            elif [ -f "$LOCAL_PATH/$FILE_SELECTED" ] ; then
                FILE_SELECTED="$LOCAL_PATH/$FILE_SELECTED"
                return 0
            fi
        fi
    done
}

The use is simple

if dr_file_select "Please, select a file" /home/user ; then
        echo "File Selected: \"$FILE_SELECTED\"."
else
        echo "Cancelled!"
fi
Score: 0

I've tried following, which worked:

whiptail --title "PPP Config" --backtitle "Welcome to SEUL" --menu YourTitle 20 80 10 `for x in $(ls -1 *.tgz); do echo $x "-"; done`

you might 9 change this into a multiple-liner as well, i've 8 added checking for empty list:

MYLIST=`for x in $(ls -1 *.tgz); do echo $x "-"; done`
WC=`echo $MYLIST | wc -l`

if [[WC -ne 0]]; then
    whiptail --title "PPP Config" --backtitle "Welcome to SEUL" --menu YourTitle 20 80 10 $MYLIST
fi

you need to 7 adjust the numbers in order to get a cleaninterface. And 6 you may replace the "-" by anything else if 5 you want to. But if you don't, you will 4 see 2 entries per line.

By the way: The selected 3 entry is printed onto stderr.

This could need some 2 more improving, but for a basic idea I think 1 it's enough.

Score: 0

This seems to be one of the top results 37 when you search for whiptail, and none of 36 the previous results worked for me. This 35 is what I wound up using:

#! /bin/bash
shopt -s nullglob
dir=`pwd`
cd /path/to/files
arr=(*.tgz)
for ((i=0; i<${#arr[@]}; i++)); do j=$((2*$i+2)); a[j]="${arr[$i]}"; a[j+1]=""; done
a[0]=""
# Next line has extra spaces at right to try to center it:
a[1]="Please make a selection from the files below                                                        "
result=$(whiptail --ok-button "OK button text" --cancel-button "Cancel Button Text" --title "Title Text" --backtitle "Text at upper left corner of page" --menu "Menu Text (not used??)" 30 160 24 "${a[@]}" 2>&1 >/dev/tty)
if [[ $? = 0 ]]
then
# ge 5 in next line should be length of file extension including . character, plus 1
  [ ${#result} -ge 5 ] && outfile="/path/to/files/$result" || echo "Selection not made"
fi
cd "$dir"

$result will be 34 empty if no valid selection was made. I 33 added a dummy selection at the top of the 32 list that returns an empty string as a result, so 31 that you won't accidentally select the wrong 30 file by accidentally hitting Enter right 29 after the menu comes up. If you don't want 28 that, then in the "for" line remove the 27 +2 in "do j=$((2*$i+2))" and also the following 26 two lines that set a[0] and a[1] explicitly.

The 25 confusing thing about whiptail is that when 24 reading from an array in a situation like 23 this it expects two data items per line, both 22 of which are displayed, the first being 21 the result you want returned if the line 20 is expected (which in some situations might 19 be a letter or a number) and the second 18 being whatever descriptive text you may 17 want. That's why for the first line I use 16 a[0] to give an empty string as the result, and 15 a[1] as the descriptive text, but from there 14 on the first item in the pair contains the 13 filename (which is what I actually want 12 returned) and the second is an empty string, since 11 I don't want to display any text other than 10 the filename on those lines.

Also a previous 9 post said whiptail returned an error code 8 of 255 if the cancel button was pressed, but 7 that was not the case for the version I 6 have - it returns 1. So I just test for 5 an error code of 0 and if it is I assume 4 it may be a valid entry, then I test for 3 a valid string length (more than just the 2 number of characters in the file extension, including 1 the . character) to be sure.

More Related questions