Martin Vegter Asked: 2018-09-11 22:41:34 +0800 CST2018-09-11 22:41:34 +0800 CST 2018-09-11 22:41:34 +0800 CST 破折号:从管道中读取变量 772 在bashorzsh中,我可以使用以下语法从管道读取变量: echo AAA BBB | read X Y ; echo $X 这将打印AAA 为什么同样不起作用/bin/sh? 我在 Debian 中使用/bin/sh-> /bin/dash dash shell dash 4 个回答 Voted Best Answer pLumo 2018-09-11T22:48:58+08:002018-09-11T22:48:58+08:00 为什么在 '/bin/sh' 中同样不起作用? 在管道中分配变量不能按预期工作sh,bash因为管道的每个命令都在子 shell 中运行。实际上,该命令确实有效X并被声明,Y但它们在管道之外不可用。 以下将起作用: echo AAA BBB | { read X Y ; echo $X; } 但在你的情况下: 尝试这个, read X Y <<< "AAA BBB" 或者 read X Y < <(echo "AAA BBB") 一些有用的链接: http://mywiki.wooledge.org/BashFAQ/024 bash:从管道分配变量? 从管道将值读入 shell 变量 ilkkachu 2018-09-12T00:27:15+08:002018-09-12T00:27:15+08:00 在 bash 或 zsh 中,我可以使用以下语法从管道读取变量。 echo AAA BBB | read X Y ; echo $X 不,你不能。默认设置下不在 Bash 中。 $ ./bash5.0-alpha -c 'echo AAA BBB | read X Y ; echo "Bash=$BASH_VERSION X=\"$X\""' Bash=5.0.0(1)-alpha X="" $ bash -c 'echo AAA BBB | read X Y ; echo "Bash=$BASH_VERSION X=\"$X\""' Bash=4.4.12(1)-release X="" Bash 在单独的子 shell 环境中运行管道中的所有命令,因此对 shell 变量的更改在管道之外不可见。破折号在这里是相似的。 Zsh 和 ksh(AT&T 实现,不是pdksh或衍生品)在主 shell 环境中运行管道的最后一个命令,因此可以正常工作: $ zsh -c 'echo AAA BBB | read X Y ; echo "Zsh=$ZSH_VERSION X=\"$X\""' Zsh=5.3.1 X="AAA" 在 Bash 中,您可以shopt -s lastpipe让它做 ksh 和 zsh 所做的事情(尽管仅适用于非交互式 shell): $ bash -O lastpipe -c 'echo AAA BBB | read X Y ; echo "Bash=$BASH_VERSION X=\"$X\""' Bash=4.4.12(1)-release X="AAA" 但我认为 Dash 没有这样的选择。 在 Bash 中,您也可以使用进程替换而不是管道,但这在 Dash 中也不是一个选项。 解决方法将围绕使循环的右侧成为复合语句或函数,因此在读取管道的相同环境中使用从管道读取的值。 $ dash -c 'echo AAA BBB | { read X Y ; echo "X=\"$X\""; } ' X="AAA" $ dash -c 'f() { read X Y ; echo "X=\"$X\""; }; echo AAA BBB | f' X="AAA" 或使用此处的文档: read X Y << EOF $(echo AAA BBB) EOF user232326 2018-09-12T01:43:52+08:002018-09-12T01:43:52+08:00 不,这不会设置 X 和 Y(在分号之后)。 echo AAA BBB | read X Y ; echo $X $ bash -c 'echo AAA BBB | read X Y ; echo "<$X>"' <> 破折号也是如此。 要让它在破折号中工作,您需要求助于较旧的解决方案(here-doc): $ read X Y <<_EOT_ > AAA BBB > _EOT_ $ echo "<$X>" <AAA> 作为尝试 shell 的命令(遗憾的是不能删除换行符(没有单行)): $ bash -c 'read X Y <<_EOT_ AAA BBB _EOT_ echo "<$X>" ' <AAA> 这在 dash、zsh、ksh 和许多其他程序中的工作方式完全相同: $ dash -c 'read X Y <<_EOT_ AAA BBB _EOT_ echo "<$X>" ' <AAA> 可能here-doc是: 这里字符串(适用于 bash、ksh、zsh): $ bash -c 'read X Y <<<"AAA BBB"; echo "<$X>" ' <AAA> 进程替换(适用于 bash、ksh、zsh): $ bash -c 'read X Y < <(echo AAA BBB) ; echo "<$X>" ' <AAA> 打印值并在 dash 中工作但不将变量设置为 dash 或 bash(但在 ksh 和 zsh 中)的替代方法是: 组命令: $ dash -c 'echo "AAA BBB" | { read X Y ; echo "<$X>"; }; echo "<$X>" ' <AAA> <> 请注意,最后一个解决方案可以设置为使用lastpipe选项(不用于交互使用)或默认情况下在 ksh/zsh 中设置变量: $ bash -c 'shopt -s lastpipe;echo "AAA BBB"|{ read X Y;echo "1<$X>"; };echo "2<$X>"' 1<AAA> 2<AAA> $ ksh -c 'echo "AAA BBB"|{ read X Y;echo "1<$X>"; };echo "2<$X>"' 1<AAA> 2<AAA> $ zsh -c 'echo "AAA BBB"|{ read X Y;echo "1<$X>"; }; echo "2<$X>"' 1<AAA> 2<AAA> schily 2018-09-11T23:45:15+08:002018-09-11T23:45:15+08:00 小心管道中进程的变量分配。POSIX 标准不需要特定的行为。 现代 shellksh93和最新版本的Bourne Shell让主 shell 成为管道中两个进程的父进程,如果最右边的进程是内置命令,这个命令甚至在主 shell 中运行。 另一种变体是使用上述方法,但始终在另一个进程中运行最右边的命令。 旧版本是原始 Bourne Shell 的工作方式:shell 分叉,分叉的进程从管道创建所有其他进程,最后转换为最右边的进程。 最后一个版本需要的代码比其他版本少得多,但速度较慢。由于代码大小,这是在 1976 年使用的。 第一个变体是最快的变体,但需要比其他变体更多的代码,但它是唯一在原始 shell 进程中运行变量赋值的变体,它需要在主 shell 中具有修改后的变量值。
在管道中分配变量不能按预期工作
sh
,bash
因为管道的每个命令都在子 shell 中运行。实际上,该命令确实有效X
并被声明,Y
但它们在管道之外不可用。以下将起作用:
但在你的情况下:
尝试这个,
或者
一些有用的链接:
不,你不能。默认设置下不在 Bash 中。
Bash 在单独的子 shell 环境中运行管道中的所有命令,因此对 shell 变量的更改在管道之外不可见。破折号在这里是相似的。
Zsh 和 ksh(AT&T 实现,不是
pdksh
或衍生品)在主 shell 环境中运行管道的最后一个命令,因此可以正常工作:在 Bash 中,您可以
shopt -s lastpipe
让它做 ksh 和 zsh 所做的事情(尽管仅适用于非交互式 shell):但我认为 Dash 没有这样的选择。
在 Bash 中,您也可以使用进程替换而不是管道,但这在 Dash 中也不是一个选项。
解决方法将围绕使循环的右侧成为复合语句或函数,因此在读取管道的相同环境中使用从管道读取的值。
或使用此处的文档:
不,这不会设置 X 和 Y(在分号之后)。
破折号也是如此。
要让它在破折号中工作,您需要求助于较旧的解决方案(here-doc):
作为尝试 shell 的命令(遗憾的是不能删除换行符(没有单行)):
这在 dash、zsh、ksh 和许多其他程序中的工作方式完全相同:
可能
here-doc
是:这里字符串(适用于 bash、ksh、zsh):
进程替换(适用于 bash、ksh、zsh):
打印值并在 dash 中工作但不将变量设置为 dash 或 bash(但在 ksh 和 zsh 中)的替代方法是:
组命令:
请注意,最后一个解决方案可以设置为使用
lastpipe
选项(不用于交互使用)或默认情况下在 ksh/zsh 中设置变量:小心管道中进程的变量分配。POSIX 标准不需要特定的行为。
现代 shell
ksh93
和最新版本的Bourne Shell
让主 shell 成为管道中两个进程的父进程,如果最右边的进程是内置命令,这个命令甚至在主 shell 中运行。另一种变体是使用上述方法,但始终在另一个进程中运行最右边的命令。
旧版本是原始 Bourne Shell 的工作方式:shell 分叉,分叉的进程从管道创建所有其他进程,最后转换为最右边的进程。
最后一个版本需要的代码比其他版本少得多,但速度较慢。由于代码大小,这是在 1976 年使用的。
第一个变体是最快的变体,但需要比其他变体更多的代码,但它是唯一在原始 shell 进程中运行变量赋值的变体,它需要在主 shell 中具有修改后的变量值。