读取命令后替换后立即在git-bash中返回,但在cygwin中不返回

发布于 2025-02-05 18:10:59 字数 2898 浏览 1 评论 0 原文

我有一个bash脚本,我用作助手脚本来生成git-bash中的差异。该脚本利用了我编写的名为菜单的程序,该程序列出了作为命令行Args中传递的选择,并提示您选择一个。在这种情况下,选择是git提交。我在我的bash脚本中使用命令替换来调用菜单程序,例如:

commit=$(menu --title "Choose a commit:" "CommitA" "CommitB" "CommitC")

菜单程序正在使用 fgets(line,256,stdin); 要读取用户的选择,并将选项打印到 stderr ,以便在我的脚本中不会被命令替换捕获。当用户选择其中一个选项时,菜单将该选项打印到 stdin ,以便可以通过命令替换来捕获。在下面的示例代码中,我将打印省略为 stdout ,因为它对后续 read read 命令的行为没有影响。之后,我提示用户使用读取内置的输出目录:

read -p "Enter the output directory: " outputDir

出于某种原因,当我在cygwin中运行它时,它可以正常工作,但是当我在git-bash中运行它时,读取立即命中EOF,不等待我输入文本。

菜单程序的代码太长在这里列出,因此我将整个内容归结为最小可重现的示例。这是示例脚本和C代码。在示例中,我没有调用我的菜单程序,而是写了一个名为提示的小C程序,该程序只是打印一个简单的提示然后做 fgets ,它或多或少地模拟了菜单程序正在做的事情。

> 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中获得的结果与git -bassh中的结果不同:

# Cygwin
> ./test.sh
fgets> foo
read> bar

# git-bash
> ./test.sh
fgets> foo
read>       # <== read returns immediately and does not wait for input

我认为命令替代可能会留下一个角色粘在 stdin buffer中,但是执行 echo -echo- n“ $ x” | wc -c 返回0,这表明没有读取输入。无论如何,在读取之后,一切都开始按预期工作。例如,如果我在命令替换后立即添加一个虚拟读,则该脚本在git-bassh中按预期工作:

> cat test.sh
x=$(./prompt)
# Dummy read to clear stdin
read
# This works fine now in git-bash
read -p "read> " x

我不知所措地解释行为上的差异。同时,我找到了一个解决方法,该解决方法是使用 timeout 读取的读取,以0.1秒的超时,以便它将从 stdin 中消耗外部 eof ,而无需要求用户击中Enter即可超越虚拟读取:

x=$(./prompt)
# Dummy read using timeout
read -t 0.1
read -p "read> " x

但是,似乎这应该可以正常工作,但没有虚拟阅读。

这是 bash 涉及的版本:

# Cygwin
> bash --version
GNU bash, version 4.4.12(3)-release (x86_64-unknown-cygwin)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

# git-bash
> bash --version
GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

我还在Ubuntu 20.04上测试了此示例,运行Bash 5.0.17,并且效果很好。

I have a bash script that I use as a helper script for generating diffs in git-bash. The script makes use of a program I wrote called menu that lists choices passed in as command line args, and prompts you to choose one. In this case, the choices are git commits. I call the menu program using command substitution in my bash script, like so:

commit=$(menu --title "Choose a commit:" "CommitA" "CommitB" "CommitC")

The menu program is using fgets(line, 256, stdin); to read the user's selection, and it prints the choices to stderr so that they won't be captured by the command substitution in my script. When the user selects one of the options, menu prints that option to stdin so it can be captured by the command substitution. In my example code below, I omit the printing to stdout, because it has no effect on the behavior of the subsequent read command. Afterwards, I prompt the user for an output directory using the read builtin:

read -p "Enter the output directory: " outputDir

For some reason, when I run this in Cygwin, it works fine, but when I run it in git-bash, the read hits EOF immediately and does not wait for me to enter text.

The code for the menu program is too long to list here, so I've boiled the whole thing down to a minimal reproducible example. Here's the example script and C code. In the example, instead of calling my menu program, I wrote a small C program called prompt, which just prints a simple prompt to stderr and then does an fgets, which more or less simulates what the menu program is doing.

> 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;
}

When I run this example, I get different results in Cygwin than I do in git-bash:

# Cygwin
> ./test.sh
fgets> foo
read> bar

# git-bash
> ./test.sh
fgets> foo
read>       # <== read returns immediately and does not wait for input

I thought perhaps the command substitution was leaving a character stuck in the stdin buffer, but doing echo -n "$x" | wc -c returns 0, which indicates no input was read. In any case, after the read, everything starts working as expected again. For instance, if I add a dummy read right after the command substitution, then the script works as expected in git-bash:

> cat test.sh
x=$(./prompt)
# Dummy read to clear stdin
read
# This works fine now in git-bash
read -p "read> " x

I am at a loss to explain this difference in behavior. In the meantime, I have found a workaround, which is to use the -t timeout option of read for the dummy read, with a timeout of 0.1 seconds, so that it will consume the extraneous EOF from stdin without requiring the user to hit Enter to get past the dummy read:

x=$(./prompt)
# Dummy read using timeout
read -t 0.1
read -p "read> " x

It seems like this should just work though, without the dummy read.

Here are the bash versions involved:

# Cygwin
> bash --version
GNU bash, version 4.4.12(3)-release (x86_64-unknown-cygwin)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

# git-bash
> bash --version
GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

I also tested this example on Ubuntu 20.04, running bash 5.0.17, and it works fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文