BASH脚本:whiptail文件选择

时间:2009-10-13 20:13:00

标签: bash select scripting

我找到了一个很棒的小程序,可以让我在我的Bash Scripts中添加用户友好的GUI;

鞭尾

然而,whiptail man page并不是那么有用,也没有提供任何示例。在做了一些谷歌搜索后,我理解如何使用whiptail创建一个简单的是/否菜单:

#! /bin/bash
# http://archives.seul.org/seul/project/Feb-1998/msg00069.html
if (whiptail --title "PPP Configuration" --backtitle "Welcome to SEUL" --yesno "
Do you want to configure your PPP connection?"  10 40 )
then 
        echo -e "\nWell, you better get busy!\n"
elif    (whiptail --title "PPP Configuration" --backtitle "Welcome to
SEUL" --yesno "           Are you sure?" 7 40)
        then
                echo -e "\nGood, because I can't do that yet!\n"
        else
                echo -e "\nToo bad, I can't do that yet\n"
fi

但我真的想建立一个文件选择菜单,使用whiptail替换我在一些不同的备份/恢复bash脚本中的旧代码:

#!/bin/bash
#This script allows you to select a file ending in the .tgz extension (in the current directory)
echo "Please Select the RESTORE FILE you would like to restore: "
   select RESTOREFILE in *.tgz; do
   break #Nothing
   done
echo "The Restore File you selected was: ${RESTOREFILE}"

我认为这必须通过whiptail的'--menu'选项来完成,但我不知道该怎么做呢? 有什么指针吗? 或者你可以指点我的一些wh examples的例子吗?

5 个答案:

答案 0 :(得分:8)

构建文件名和菜单选择标记数组:

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

即使存在带空格的文件名,这样的方法也能正常工作。如果文件数量很大,您可能需要设计另一种标记策略。

使用标记的字母字符可以按一个字母跳转到该项目。数字标签似乎没有这样做。如果您不需要这种行为,那么您可以消除一些复杂性。

显示菜单:

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

如果退出代码为255,则对话框被取消。

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

要捕获变量中的选择,请使用此结构(将whiptail命令替换为“whiptail-command”):

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

或者

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

变量$result将包含与数组中的文件对应的字母表字母。不幸的是,版本4之前的Bash不支持关联数组。您可以从这样的字母计算文件数组的索引(注意“额外”单引号):

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

示例:

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>        │
                │                                      │
                └──────────────────────────────────────┘

答案 1 :(得分:3)

Whiptail是使用dialog轻量级重新实现Newt library最受欢迎的功能。我做了一个快速检查,Whiptail中的许多功能似乎与对话框中的对应物一样。因此,对话教程应该可以帮助您入门。您可以找到一个here,但Google当然是您的朋友。另一方面,extended example可能包含很多你的问题的灵感。

答案 2 :(得分:0)

我尝试过以下工作:

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

您也可以将其更改为多行,我已添加检查空列表:

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

您需要调整数字以获得清洁界面。如果您愿意,可以用其他任何内容替换"-"。但如果你不这样做,你会看到每行2个条目。

顺便说一下:所选条目将打印到stderr

这可能需要更多改进,但对于一个基本想法,我认为这已经足够了。

答案 3 :(得分:0)

当你搜索whiptail时,这似乎是最好的结果之一,而且以前的结果都不适合我。这就是我最终使用的内容:

#! /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将为空。我在列表顶部添加了一个虚拟选择,结果返回一个空字符串,因此在菜单出现后不小心点击Enter键不会意外选择错误的文件。如果你不想那样,那么在“for”行中删除“do j = $((2 * $ i + 2))”中的+2以及设置[0]和a的以下两行[1]明确。

关于whiptail的令人困惑的事情是,当在这种情况下从数组中读取时,它预计每行显示两个数据项,这两个数据项都显示出来,第一个是你想要返回的结果(如果是预期的那行)某些情况可能是字母或数字),第二种情况可能是您想要的任何描述性文字。这就是为什么对于第一行我使用[0]给出一个空字符串作为结果,并使用[1]作为描述性文本,但从那里开始,该对中的第一项包含文件名(这就是我实际想要返回),第二个是空字符串,因为我不想在这些行上显示文件名以外的任何文本。

另外一篇帖子说,如果按下取消按钮,whiptail返回错误代码255,但是我的版本不是这样 - 它返回1.所以我只测试错误代码为0,如果它是我认为它可能是一个有效的条目,然后我测试一个有效的字符串长度(不仅仅是文件扩展名中的字符数,包括。字符),以确保。

答案 4 :(得分:0)

该函数是我的wiptail函数存储库的一部分

# ----------------------------------------------------------------------
#  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
}

使用很简单

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