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

OSX系统Bash脚本循环中read命令失效问题求助

问题根源:管道创建的子Shell导致read读取错误输入

你的问题核心出在管道(|)会创建子Shell,而你的两层while read循环都在子Shell中运行:

  • 子Shell的标准输入(stdin)被管道的输出(也就是find命令的结果)占据了
  • 当你在内部循环里调用read -p ...时,它并没有从终端读取用户输入,而是直接从管道的剩余输出里取了一行内容,看起来就像read被“忽略”了

另外,你的代码里还有个小错误:int=int+1不是Bash的整数加法写法,应该改成((int++))或者int=$((int+1)),否则int会变成字符串"int+1",后面的[ "$int" -gt 44 ]会触发语法错误。


解决方案

下面提供两种可行的修复方式:

方式1:用进程替换替代管道(推荐)

进程替换(< <(command))可以让循环在当前Shell中执行,而非子Shell,这样read会默认从终端读取输入,同时变量修改也能保留到父Shell:

declare -i int
int=1
# 用进程替换代替管道,循环在当前shell执行
while read FILENAME
do
    # 内部循环同样用进程替换
    while read FILENAME2
    do
        if [ "$FILENAME" != "$FILENAME2" ]; then
            if [ "$(dirname "${FILENAME}")" != "$(dirname "${FILENAME2}")" ]; then
                if [ "$(basename "${FILENAME}")" == "$(basename "${FILENAME2}")" ]; then
                    ((int++))  # 正确的整数自增写法
                    if cmp -s "$FILENAME" "$FILENAME2" ; then
                        echo "Match", "$FILENAME", "$FILENAME2", "$int"
                    else
                        read -p "No match modify? (y/n)" d
                        echo "$d"
                    fi
                    if [ "$int" -gt 44 ]; then
                        exit 1
                    fi
                fi
            fi
        fi
    done < <(find . -name '*.xyz')
done < <(find ./1 -name '*.xyz')

方式2:强制read从终端读取输入

如果你不想改动管道结构,可以给read命令显式指定输入源为终端设备/dev/tty,这样即使在子Shell里,read也会从终端获取用户输入:

declare -i int
int=1
find ./1 -name '*.xyz' | while read FILENAME
do
    find . -name '*.xyz' | while read FILENAME2
    do
        if [ "$FILENAME" != "$FILENAME2" ]; then
            if [ "$(dirname "${FILENAME}")" != "$(dirname "${FILENAME2}")" ]; then
                if [ "$(basename "${FILENAME}")" == "$(basename "${FILENAME2}")" ]; then
                    ((int++))
                    if cmp -s "$FILENAME" "$FILENAME2" ; then
                        echo "Match", "$FILENAME", "$FILENAME2", "$int"
                    else
                        # 强制read从终端读取
                        read -p "No match modify? (y/n)" d < /dev/tty
                        echo "$d"
                    fi
                    if [ "$int" -gt 44 ]; then
                        exit 1
                    fi
                fi
            fi
        fi
    done
done

注意事项
  • 方式1的进程替换是Bash专属特性,macOS的iTerm默认使用Bash,所以完全兼容;如果是POSIX sh环境则无法使用
  • 方式2虽然简单,但子Shell里的int变量修改不会传递到父Shell,不过你的代码里int仅用于计数退出,这个场景下影响不大
  • 建议给所有变量引用加上双引号(比如"$FILENAME"),避免文件名包含空格或特殊字符时触发语法错误

内容的提问来源于stack exchange,提问作者Raymond gsh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:02:23