为什么我不能使用 ConEmu 在 Windows 上将输入通过管道传输到 Cygwin 中的 Bash 中的“read”命令中?

为什么我不能使用 ConEmu 在 Windows 上将输入通过管道传输到 Cygwin 中的 Bash 中的“read”命令中?

我正在尝试将管道的输出存储到变量中。

读完这篇文章后(使用“read”访问 Bash 管道的输出), 我有些怀疑我是否曾在 Unix 系统上成功做到这一点。

然后我发现了这篇文章(如何循环遍历 find 返回的文件名?),并被提醒说它有效,有时使用read -r <varname>也一样。

环境:

User@Computer /cygdrive/...
$ echo $SHELL
/bin/bash

User@Computer /cygdrive/...
$ echo $BASH_VERSION
4.4.5(1)-release

User@Computer /cygdrive/...
$ uname -a
CYGWIN_NT-10.0 Computer 2.6.1(0.305/5/3) 2016-12-16 11:55 x86_64 

User@Computer /cygdrive/...
$ read --help
read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
    Read a line from the standard input and split it into fields.

尝试:

User@Computer /cygdrive/...
$ echo $var


User@Computer /cygdrive/...
$ echo "hi" | read var

User@Computer /cygdrive/...
$ echo $var


User@Computer /cygdrive/...
$ echo "hi" | read -r var

User@Computer /cygdrive/...
$ echo $var

User@Computer /cygdrive/...
$ read var
hi

User@Computer /cygdrive/...
$ echo $var
hi

User@Computer /cygdrive/...
$ echo "hi" | cat
hi

User@Computer /cygdrive/...
$ echo -e '1\n2\n3\n' | xargs
1 2 3

我也在bash.exe自己的进程和窗口中尝试过这个:

User@Computer /cygdrive/...
$ echo "hi" | read var;echo $var;

在“Windows 上的 Ubuntu 上的 Bash”中也执行同样操作bash.exe

echo "hi" | read var;echo $var;

任务仍然没有成功完成。我知道我可以绕过这个方法,或者使用$()重定向箭头。

有没有办法read在 Windows 上使用此行命令方法来执行此操作?

更新

对于某些答案,我知道有些人会发布类似的替代方案$()。 这就是为什么我故意提到这种方法,包括一个已经列出几种方法的链接,并特别询问是否有办法使用readline 实用程序将输入存储到变量中,而不是使用重定向,而是通过命令链传输数据,就像我以前在其他系统上所做的那样,但这次是在 Windows 上。

答案1

对于单行结果,在 bash 中执行此操作的规范方法是:

variable=$(the_command the_command_args)

例如:

today=$(date +%Y%m%d) 

(并且如果需要,命令参数可以包含$-变量)。

对于多矿结果,您可以使用循环依次处理每一行:

for f in $(ls -1)
do
    echo @@@@"$f"
done

答案2

TL:DR - 在 bash 中,只有管道序列中的第一个命令在当前 shell 中执行。

在某些其他 shell 中,例如 ksh,最后一个命令将在当前 shell 中执行。

因为read它是 shell 内置命令,它允许您在活动环境(例如当前 shell)中设置变量。

例如

read value
echo $value

但是当读取位于管道序列的末尾时,bash 将在当前 shell 中运行第一个命令,并read在其他 shell 中运行所有其他命令(包括)。

echo 10 | read value

此处valuevar 不会传递到当前 shell(导出的变量只能传递给子 shell)

解决方案是不使用管道。

read value <<<$(echo 10)

这是可行的read,因为第一个命令将在当前 shell 中运行。

另一个解决方案是使用临时文件

command1 | command2 | command3 > /tmp/tempfile
read value < /tmp/tempfile

特别说明:

如果仅分配一个变量,则可以使用具有值替换的赋值,例如:

datetoday=$(date)

(或者我最讨厌使用反引号做同样的事情 - datetoday=`date`。)

但是在分配多个变量时这不是一个选项,例如同时为 3 个变量分配值:

read year month day <<<$(date '+%Y %m %d')
echo Year: $year
echo Month: $month
echo Day: $day

答案3

您可以使用命名管道。

例子:

sharuzzaman@laptop /cygdrive/...
$ mkfifo mypipe

sharuzzaman@laptop /cygdrive/...
$ echo "hi" > mypipe &
[1] 5260

sharuzzaman@laptop /cygdrive/...
$ read asdf < mypipe

sharuzzaman@laptop /cygdrive/...
$ echo $asdf
hi
[1]+  Done                    echo "hi" > mypipe

答案4

对于其他答案,它们看起来像是一些很好的例子的巧妙方法,但我真的很好奇为什么我一直临时使用的这种方法和一些 shell 脚本“停止”工作,或者在我的 Windows 系统上不起作用。

我没有看到他们发布任何直接与如何使用read和管道实现这一点相关的答案,因此这里是我的更新,作为一种闭包形式,我测试了一些示例并突出显示了提到的特定功能。

好吧,对于这个问题的评论,我可以在我的 Mac (10.12.5) 系统、iTerm 3.0.15 和 shell 中展示这个工作原理fish

⋊> ~ echo "hi" | read var;echo $var;
hi
⋊> ~ fish -v
fish, version 2.6.0

当他们引用正在使用的子 shell 时,它似乎在工作fish,我有点理解它具有不同的架构和设计模式,尽管在某些部分我会期望类似功能的层,至少从用例的角度来看。

然后我在 Mac 上使用两个解释器再次尝试了该方法。

fish

⋊> ~ echo -ne '1\n2\n3\n' | while read line; echo "a$line""a"; end
a1a
a2a
a3a

bash

bash-3.2$ echo -ne '1\n2\n3\n' | while read line; do echo "a$line""a"; done
a1a
a2a
a3a

bash-3.2$ echo $BASH
/bin/bash
bash-3.2$ echo $BASH_VERSION
3.2.57(1)-release

所有功能似乎(大部分)都按预期工作。我有点惊讶地发现变量没有输出bash,但我认为我最初的问题来自于没有获得预期的数据来持续到那个循环。我必须再次考虑到所有这些,更仔细地检查我的代码。

最终结果

这里要强调的重点是不要通过管道将标准输入存储到简单变量中bash,并在脚本中继续正常运行,因为根据沙鲁扎曼·阿赫马特·拉斯兰戴夫·汤普森

相反,它确实以这种方式工作fish,或者我可以更加小心地直接在带有 while 循环的管链中工作,然后回去检查我的代码是否存在这些问题。

其他说明

我认为我的代码行中的另一个问题是我试图使用read -r <varname>,但最终却输入了read -p <varname>。后者使用了一个额外的参数来形成 read 命令的提示,在我的例子中,只有 1 个参数,没有留下有用的变量名来存储信息并从中检索信息。前者有时在其他例子中很有用,当不想使用退格转义序列时,无论何时。所以要避免像这样的拼写错误!

相关内容