You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 19:17:15