在bash中处理select结构的列表

时间:2017-01-21 22:42:58

标签: bash

我正在编写一个bash脚本,用户必须从列表中选择一个选项。选项列表实际上是目录中文件的基本名称列表(没有路径,没有扩展名),以及应排除符号链接的附加要求。

我喜欢张贴在this other question中的一个衬垫,最初用于选择子目录:

printf "Please select folder:\n"
select d in */; do test -n "$d" && break; echo ">>> Invalid Selection"; done
cd "$d" && pwd

我知道如何获取文件的basename,以及如何检查文件是否不是符号链接,而是文件的文件。但是,select构造管理文件列表。我可以以某种方式将basename命令和! -L检查动态插入select构造中的所有元素,从而在列表到达select之前对其进行过滤和处理?

2 个答案:

答案 0 :(得分:2)

假设您的文件名没有嵌入的换行符,这很少引起关注):

#!/usr/bin/env bash

# Collect the sorted names of all regular files in the current directory
# in array ${files[@]}.
IFS=$'\n' read -d '' -ra files < <(find . -maxdepth 1 -type f | cut -d/ -f2 | sort)
# Bash 4.x would allow the simpler:
# readarray -t files < ...

select f in "${files[@]}"; do
  [[ -n "$f" ]] && break # Valid selection made, exit the menu.
  echo ">>> Invalid Selection" >&2
done

# "$f" contains the selected file.

请注意,根据您的平台和find实施情况,这可以简化。

-type f仅匹配常规文件并自动排除符号链接。

答案 1 :(得分:1)

避免基本原理的次优方法(尽管您的问题明确要求bash解决方案),而且避免依赖于未内置到shell中的任何工具:

#!/bin/sh
set --                        ## clear our argument list to reuse it

printf "Please select folder:\n"
for d in */; do               ## iterate only over directories
  [ -L "${d}%/" ] && continue ## skip to next item if we have a link
  d=${d%/}; d=${d##*/}        ## alternative to basename using only parameter expansion
  set -- "$@" "$d"            ## append our result to "$@"
done
select d in "$@"; do          ## ...and then expand "$@" to select over
  cd "$d" && pwd
done

值得注意的是:

  • "$@"是保证在基线POSIX sh中可用的唯一数组。请注意,每个堆栈帧都提供了不同的参数列表,因此通过将此代码封装在函数中,您可以避免覆盖全局"$@"
  • 如果我们为bash编写这个,我们可以通过array+=( "$value" )将数组追加作为O(1)操作,而不是O(n)set -- "$@" "$value"(需要扩展当前列表) ,因此现有的参数列表lengnh增长得慢。