Git Bash命令替换后read立即返回EOF 但Cygwin运行正常
Git Bash环境下命令替换后首个read命令立即触发EOF问题
问题背景
- 开发了一款用于在Git Bash中生成代码diff的bash辅助脚本,脚本中调用自研程序
menu实现交互选择:menu接收命令行参数传入的选项列表(本次场景下选项为Git提交记录),提示用户完成选择- 脚本通过命令替换方式调用
menu,调用形式如下:commit=$(menu --title "Choose a commit:" "CommitA" "CommitB" "CommitC") menu内部通过fgets(line, 256, stdin);读取用户输入的选择,所有选项列表输出到stderr,避免被命令替换捕获;用户选定的结果输出到stdout供命令替换捕获(后续最小复现样例中省略了向stdout输出结果的逻辑,该逻辑不会影响后续read命令的行为)
- 命令替换执行完成后,脚本通过bash内置
read命令提示用户输入输出目录:read -p "Enter the output directory: " outputDir - 上述逻辑在Cygwin环境下运行完全正常,但在Git Bash环境中运行时,
read命令会立即触发EOF,不会等待用户输入文本。
最小复现样例
由于menu程序代码量较大不便完整展示,构造了名为prompt的极简C程序替代menu,仅向stderr输出简单提示后调用fgets读取输入,模拟menu的核心行为,对应的测试脚本和C代码如下:
> cat test.sh x=$(./prompt) read -p "read> " x > cat prompt.c #include <stdio.h> int main(int argc, char **argv) { char line[256]; fprintf(stderr, "fgets> "); fflush(stderr); fgets(line, 256, stdin); return 0; }
跨环境运行差异
不同环境下运行测试脚本的结果存在明显差异:
- Cygwin环境运行结果:
> ./test.sh fgets> foo read> bar - Git Bash环境运行结果:
> ./test.sh fgets> foo read> # <== read立即返回,不等待用户输入
初步排查与临时方案
- 最初猜测是命令替换执行后stdin缓冲区残留字符,但执行
echo -n "$x" | wc -c返回0,证明没有读取到残留输入。 - 进一步测试发现,该异常仅出现在命令替换后的第一个
read调用,后续的read可正常工作:如果在命令替换后新增一个空的占位read,Git Bash下后续的read即可正常等待输入:> cat test.sh x=$(./prompt) # 占位read用于清空stdin read # 该read在Git Bash下可正常工作 read -p "read> " x - 目前找到的临时规避方案:为占位read添加
-t 0.1的超时参数,即可消耗stdin中多余的EOF,无需用户额外按回车即可跳过占位read:
该方案属于临时规避手段,理论上无需添加占位read脚本即可正常运行。x=$(./prompt) # 带超时的占位read read -t 0.1 read -p "read> " x
涉及环境版本信息
- Cygwin环境:bash版本为4.4.12(3)-release (x86_64-unknown-cygwin),运行正常
- Git Bash环境:bash版本为4.4.23(1)-release (x86_64-pc-msys),存在上述异常
- Ubuntu 20.04环境(bash 5.0.17):运行正常
内容的提问来源于stack exchange,提问作者Mike Holt
相关产品推荐
相关产品推荐

