![根据文件列表创建 bash 菜单(将文件映射到数字)](https://linux22.com/image/876547/%E6%A0%B9%E6%8D%AE%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8%E5%88%9B%E5%BB%BA%20bash%20%E8%8F%9C%E5%8D%95%EF%BC%88%E5%B0%86%E6%96%87%E4%BB%B6%E6%98%A0%E5%B0%84%E5%88%B0%E6%95%B0%E5%AD%97%EF%BC%89.png)
这答案展示了出色且直观的 bash 菜单只需按数字即可选择项目。但这对于文件列表来说有点不方便,因为它都是硬编码的。我宁愿将文件填充到某种数组中,然后让用户选择一个再次映射到数组偏移量的数字。
基本上这就是我所想象的:
Following `*.war` archives were found, select one:
1) old.war
2) debug.war
3) release.war
Use number to select a file or 'stop' to cancel: blah
'blah' is not a number
Use number to select a file or 'stop' to cancel: 2
debug.war installed
但是我如何将文件列表转换成这个数组:
options=("Option 1" "Option 2" "Option 3" "Quit")
如何在 中获取特定偏移量的字符串options
?如何确保要求用户重试?我可以允许字符串stop
停止选择模式吗?
答案1
要将输出保存find
在bash
数组中,请使用以下命令:
unset options i
while IFS= read -r -d $'\0' f; do
options[i++]="$f"
done < <(find /dir/ -maxdepth 1 -type f -name "*.war" -print0 )
read
find
从空分隔符 ( )读取输入-d $'\0'
。- 数组
$options
中填充的是文件名。
- 数组
find
-type f
仅在给定目录 ( ) 中搜索以 (-maxdepth 1
) 结尾的文件.war
(-name "*.war"
),并用空字符 (-print0
) 分隔符打印它们。
选择菜单可以像这样完成:
select opt in "${options[@]}" "Stop the script"; do
case $opt in
*.war)
echo "War file $opt selected"
# processing
;;
"Stop the script")
echo "You chose to stop"
break
;;
*)
echo "This is not a number"
;;
esac
done
其工作原理如下:
1) /dir/old.war
2) /dir/debug.war
3) /dir/release.war
4) Stop the script
#? test
This is not a number
#? 2
War file /dir/debug.war selected
#? 4
You chose to stop
答案2
您还可以使用 shell glob 来获取文件列表。此方法的优点是无需使用外部程序 ( find
),并且不需要对文件类型进行任何限制 (*war
例如 ),如下所示:
#!/usr/bin/env bash
## Collect the files in the array $files
files=( ~/foo/war/* )
## Enable extended globbing. This lets us use @(foo|bar) to
## match either 'foo' or 'bar'.
shopt -s extglob
## Start building the string to match against.
string="@(${files[0]}"
## Add the rest of the files to the string
for((i=1;i<${#files[@]};i++))
do
string+="|${files[$i]}"
done
## Close the parenthesis. $string is now @(file1|file2|...|fileN)
string+=")"
## Show the menu. This will list all files and the string "quit"
select file in "${files[@]}" "quit"
do
case $file in
## If the choice is one of the files (if it matches $string)
$string)
## Do something here
echo "$file"
## Uncomment this line if you don't want the menu to
## be shown again
# break;
;;
"quit")
## Exit
exit;;
*)
file=""
echo "Please choose a number from 1 to $((${#files[@]}+1))";;
esac
done
答案3
select
可以为您完成大部分工作,无需付出太多努力。
我如何将文件列表转换为这个数组?
您实际上不需要。select
需要一系列单词作为选项显示。这些可以直接给出 ( select color in red green blue
) 或来自文件 glob 的扩展 ( select file in *.war
),以及将数组扩展为单词,就像您找到的示例一样 ( select option in "${options[@]}"
)。
如何在选项中获取特定偏移量的字符串?
select
自动执行此操作,并将其存储到您提供的变量中。(如果用户的输入无效,则存储空字符串。)
我如何确保要求用户再试一次?
Againselect
为你做到这一点,因为选择循环,例如while
。它会一直询问,直到你break
退出循环(或者直到它读到 EOF,通常通过Ctrl+输入D)。
我可以允许字符串
stop
停止选择模式吗?
是的,用户的输入被放入变量中REPLY
,无论它是否对应其中一个选项,因此您可以检查特定的值并以不同的方式处理它们。
综合起来:
echo "The following `*.war` archives were found; select one:"
# set the prompt used by select, replacing "#?"
PS3="Use number to select a file or 'stop' to cancel: "
# allow the user to choose a file
select filename in *.war
do
# leave the loop if the user says 'stop'
if [[ "$REPLY" == stop ]]; then break; fi
# complain if no file was selected, and loop to ask again
if [[ "$filename" == "" ]]
then
echo "'$REPLY' is not a valid number"
continue
fi
# now we can use the selected file
echo "$filename installed"
# it'll ask for another unless we leave the loop
break
done