我正在编写一个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
之前对其进行过滤和处理?
答案 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中可用的唯一数组。请注意,每个堆栈帧都提供了不同的参数列表,因此通过将此代码封装在函数中,您可以避免覆盖全局"$@"
。array+=( "$value" )
将数组追加作为O(1)操作,而不是O(n)set -- "$@" "$value"
(需要扩展当前列表) ,因此现有的参数列表lengnh增长得慢。