根据文件列表创建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