根据文件列表创建 bash 菜单(将文件映射到数字)

根据文件列表创建 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停止选择模式吗?

答案1

要将输出保存findbash数组中,请使用以下命令:

unset options i
while IFS= read -r -d $'\0' f; do
  options[i++]="$f"
done < <(find /dir/ -maxdepth 1 -type f -name "*.war" -print0 )
  • readfind从空分隔符 ( )读取输入-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

相关内容