BASH 脚本:whiptail 文件选择
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1562666/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
BASH scripts : whiptail file select
提问by BassKozz
I've found a great little program that will allow me to add user friendly GUI's to my Bash Scripts;
我找到了一个很棒的小程序,它可以让我将用户友好的 GUI 添加到我的 Bash 脚本中;
whiptail
鞭尾
However the whiptail man pageisn't all that helpful and doesn't provide any examples. After doing some google searches I understand how to create a simple yes/no menu using whiptail:
然而,whiptail 手册页并不是那么有用,也没有提供任何示例。做了一些谷歌搜索后,我明白如何使用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
But what I would really like to build a file select menu using whiptail to replace some old code I have in a few different backup/restore bash scripts I have:
但是我真的很想使用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}"
I assume this has to be done via the '--menu' option of whiptail, but I am not sure how to go about it? Any pointers? Or can you point me in the direction of some whiptail examples?
我认为这必须通过whiptail的'--menu'选项来完成,但我不知道如何去做?任何指针?或者你能指出一些鞭尾例子的方向吗?
回答by Paused until further notice.
Build an array of file names and menu select 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 "whiptail --backtitle "Welcome to SEUL" --title "Restore Files" \
--menu "Please select the file to restore" 14 40 6 "${files[@]}"
$(( $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 there are filenames with spaces. If the number of files is large, you may have to devise another tag strategy.
即使文件名带有空格,这样的方法也能工作。如果文件数量很大,您可能需要设计另一种标记策略。
Using alpha characters for the tags lets you press a letter to jump to the item. Numeric tags don't seem to do that. If you don't need that behavior, then you can eliminate some complexity.
对标签使用字母字符可让您按字母跳转到项目。数字标签似乎没有这样做。如果您不需要这种行为,那么您可以消除一些复杂性。
Display the menu:
显示菜单:
if [[ $? == 255 ]]
then
do cancel stuff
fi
If the exit code is 255, the dialog was canceled.
如果退出代码是 255,则对话框被取消。
result=$(whiptail-command 2>&1 >/dev/tty)
To catch the selection in a variable, use this structure (substitute your whiptail command for "whiptail-command"):
要捕获变量中的选择,请使用此结构(将您的whiptail 命令替换为“whiptail-command”):
result=$(whiptail-command 3>&2 2>&1 1>&3-)
Or
或者
((index = 2 * ( $( printf "%d" "'$result" ) - 65 ) + 1 ))
The variable $resultwill contain a letter of the alphabet that corresponds to a file in the array. Unfortunately, Bash prior to version 4 doesn't support associative arrays. You can calculate the index into the array of the file from the letter like this (notice the "extra" single quote):
该变量$result将包含与数组中的文件相对应的字母表。不幸的是,版本 4 之前的 Bash 不支持关联数组。您可以像这样从字母计算文件数组的索引(注意“额外”单引号):
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> │
│ │
└──────────────────────────────────────┘
Example:
例子:
whiptail --title "PPP Config" --backtitle "Welcome to SEUL" --menu YourTitle 20 80 10 `for x in $(ls -1 *.tgz); do echo $x "-"; done`
回答by fvu
Whiptail is a lightweight reimplementation of the most popular features of dialog, using the Newt library. I did a quick check, and many features in Whiptail seem to behave like their counterparts in dialog. So, a dialog tutorial should get you started. You can find one herebut Google is your friend of course. On the other hand, the extended exampleprobably contains a lot of inspiration for your problem.
Whiptail 是最流行的对话框功能的轻量级重新实现,使用Newt 库。我做了一个快速检查,Whiptail 中的许多功能似乎在对话中表现得像它们的对应物。因此,对话教程应该可以帮助您入门。你可以在这里找到一个,但谷歌当然是你的朋友。另一方面,扩展示例可能对您的问题有很多启发。
回答by Atmocreations
I've tried following, which worked:
我试过以下方法,效果很好:
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 might change this into a multiple-liner as well, i've added checking for empty list:
您也可以将其更改为多行,我已添加检查空列表:
#! /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"
you need to adjust the numbers in order to get a cleaninterface. And you may replace the "-"by anything else if you want to. But if you don't, you will see 2 entries per line.
您需要调整数字以获得干净的界面。"-"如果您愿意,您可以用其他任何东西替换。但如果不这样做,您将看到每行 2 个条目。
By the way: The selected entry is printed onto stderr.
顺便说一句:所选条目打印到stderr.
This could need some more improving, but for a basic idea I think it's enough.
这可能需要更多的改进,但对于一个基本的想法,我认为这已经足够了。
回答by Skyviewer
This seems to be one of the top results when you search for whiptail, and none of the previous results worked for me. This is what I wound up using:
当您搜索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
}
$result will be empty if no valid selection was made. I added a dummy selection at the top of the list that returns an empty string as a result, so that you won't accidentally select the wrong file by accidentally hitting Enter right after the menu comes up. If you don't want that, then in the "for" line remove the +2 in "do j=$((2*$i+2))" and also the following two lines that set a[0] and a[1] explicitly.
如果未进行有效选择,则 $result 将为空。我在列表顶部添加了一个虚拟选择,结果返回一个空字符串,这样您就不会在菜单出现后不小心按 Enter 键而意外选择了错误的文件。如果您不想要那样,则在“for”行中删除“do j=$((2*$i+2))”中的 +2 以及以下设置 a[0] 和 a [1] 明确。
The confusing thing about whiptail is that when reading from an array in a situation like this it expects two data items per line, both of which are displayed, the first being the result you want returned if the line is expected (which in some situations might be a letter or a number) and the second being whatever descriptive text you may want. That's why for the first line I use a[0] to give an empty string as the result, and a[1] as the descriptive text, but from there on the first item in the pair contains the filename (which is what I actually want returned) and the second is an empty string, since I don't want to display any text other than the filename on those lines.
关于whiptail的令人困惑的事情是,在这种情况下从数组中读取时,它期望每行有两个数据项,都显示,第一个是您希望返回的结果,如果该行是预期的(在某些情况下可能是一个字母或数字),第二个是您可能想要的任何描述性文本。这就是为什么对于第一行,我使用 a[0] 作为结果给出一个空字符串,并使用 a[1] 作为描述性文本,但从那里开始,该对中的第一项包含文件名(这是我实际上想要返回),第二个是一个空字符串,因为我不想在这些行上显示除文件名之外的任何文本。
Also a previous post said whiptail returned an error code of 255 if the cancel button was pressed, but that was not the case for the version I have - it returns 1. So I just test for an error code of 0 and if it is I assume it may be a valid entry, then I test for a valid string length (more than just the number of characters in the file extension, including the . character) to be sure.
之前的一篇文章还说,如果按下取消按钮,whiptail 返回错误代码 255,但我的版本并非如此 - 它返回 1。所以我只测试错误代码 0,如果是我假设它可能是一个有效的条目,然后我测试一个有效的字符串长度(不仅仅是文件扩展名中的字符数,包括 . 字符)以确保。
回答by Federico Firenze
This function is part of my function repository for whiptail
这个函数是我的whiptail函数库的一部分
if dr_file_select "Please, select a file" /home/user ; then
echo "File Selected: \"$FILE_SELECTED\"."
else
echo "Cancelled!"
fi
The use is simple
使用简单
##代码##
