根据文件列表创建bash菜单(将文件映射到数字)
这个答案显示了优秀而直观的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
以停止选择模式吗?
要在bash
数组中保存find
的输出,请使用以下命令:
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
null delimited(-d $'\0'
)的输入。- 数组
$options
用文件名填充。
- 数组
-
find
仅搜索给定目录(-maxdepth 1
)中的文件(-type f
),并以.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
您还可以使用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
select
可以为您完成大部分工作,无需太多努力。
如何将文件列表转换为此数组?
你实际上并不需要。 select
将一系列单词显示为选项。 这些可以直接给出( select color in red green blue
)或来自文件glob的扩展( select file in *.war
),以及将数组扩展为单词,如您找到的示例所做的那样( select option in "${options[@]}"
)。
如何在选项中的某个偏移处获取字符串?
select
会自动执行此操作,并将其存储到您提供的变量中。 (如果用户输入无效,则存储空字符串。)
如何确保要求用户再试一次?
再次select
为你做这个,因为select会像一样循环 。 它将一直询问,直到你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