为何Bash的test函数在while循环中无法正确识别目录?
Bash函数内
[[ -d ]]判断目录失败的原因 原始函数代码
function cdloc () { local dir="${1}" local subdir="${2}" local found="" if isnull "${2}" ; then subdir=$(basename "${dir}") dir=$(dirname "${dir}") fi while read -r target ; do echo "cdloc trying '${target}'" set -xv if [[ -d ${target} ]]; then echo "cdloc: found matching directory '${target}'" #cd "${target}" # return 0 else echo "cdloc: skipping non-directory '${target}'" fi set +xv done < <(ls -d "${dir}"/*"${subdir}"* 2>/dev/null) echo "Could not find match for dir \"${dir}\" and subdir \"${subdir}\", even using pattern matching" }
测试案例
rcook@MacBook-Pro-2021 (obsidian-image-resizer-plugin (BARE:master)): mkdir -p foo/bar rcook@MacBook-Pro-2021 (obsidian-image-resizer-plugin (BARE:master)): cdloc foo bar cdloc trying 'foo/bar/' +(/Users/rcook/Projects/RC-bash-lib/shellfuncs.sh:13): cdloc(): [[ -d foo/bar/ ]] +(/Users/rcook/Projects/RC-bash-lib/shellfuncs.sh:18): cdloc(): echo 'cdloc: skipping non-directory '\''foo/bar/'\'' cdloc: skipping non-directory 'foo/bar/' +(/Users/rcook/Projects/RC-bash-lib/shellfuncs.sh:20): cdloc(): set +xv Could not find match for dir "foo" and subdir "bar", even using pattern matching rcook@MacBook-Pro-2021 (obsidian-image-resizer-plugin (BARE:master)): # Wait what? rcook@MacBook-Pro-2021 (obsidian-image-resizer-plugin (BARE:master)): if [[ -d foo/bar/ ]] ;then echo yes; fi yes
核心疑问
为何[[ -d foo/bar/ ]]在函数内判断失败,直接在终端执行却返回成功?
问题根源
罪魁祸首是**ls的颜色输出**:
- 若系统中给
ls设置了别名(比如macOS默认的alias ls='ls -G',或Linux的alias ls='ls --color=auto'),ls会给目录名添加ANSI颜色控制序列(用于高亮显示的不可见转义字符)。 - 这些转义字符会被
read命令一同读取进target变量,导致[[ -d ${target} ]]实际判断的是包含无效字符的错误路径,自然返回false。 - 直接在终端执行判断时,输入的是纯路径字符串,无转义字符,因此判断正常。
注:ls -d输出的末尾斜杠本身不是问题——Bash的[[ -d ]]对带末尾斜杠的有效目录会正常返回true,真正的问题是隐藏的颜色转义码。
解决办法
改用find命令替代ls是最可靠的方案(如你已测试的版本),因为find默认不会输出任何格式控制字符,仅返回纯路径:
function cdloc () { local dir="${1}" local subdir="${2}" local found="" if isnull "${2}" ; then subdir=$(basename "${dir}") dir=$(dirname "${dir}") fi while read -r target ; do echo "cdloc trying '${target}'" #set -xv if [[ -d "${target}" ]]; then echo "cdloc: found matching directory '${target}'" cd "${target}" return 0 else echo "cdloc: skipping non-directory '${target}'" fi #set +xv done < <(find "${dir}" -depth 1 -name "*${subdir}*" 2>/dev/null) echo "Could not find match for dir \"${dir}\" and subdir \"${subdir}\", even using pattern matching" return 1 }
额外优化:给[[ -d ]]中的${target}加上双引号,避免路径包含空格时出现解析错误。
内容的提问来源于stack exchange,提问作者Rich
相关产品推荐
相关产品推荐

