在bash
or中zsh
,我可以使用以下语法从管道读入变量:
echo AAA BBB | read X Y ; echo $X
这将打印AAA
为什么同样的方法在 中不起作用/bin/sh
?
我正在使用/bin/sh
->/bin/dash
短跑在 Debian 中
答案1
为什么同样的方法在 '/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")
一些有用的链接:
答案2
在 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"
但我认为达世币没有这样的选择。
在 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
答案3
不,这不会设置 X 和 Y(分号之后)。
echo AAA BBB | read X Y ; echo $X
$ bash -c 'echo AAA BBB | read X Y ; echo "<$X>"'
<>
同样的情况也发生在破折号中。
要让它在仪表板中工作,您需要诉诸旧的解决方案(此处文档):
$ 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 -c 'echo "AAA BBB" | { read X Y ; echo "<$X>"; }; echo "<$X>" ' <AAA> <>
请注意,最后一个解决方案可以设置为保留 bash 中设置的变量
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>
答案4
请小心管道中进程的变量分配。 POSIX 标准不要求特定的行为。
现代 shellksh93
和最新版本的 shellBourne Shell
让主 shell 成为管道中两个进程的父进程,如果最右边的进程是内置命令,则该命令甚至在主 shell 中运行。
另一种变体是使用上述方法,但始终在另一个进程中运行最右边的命令。
旧版本是原始 Bourne Shell 的工作方式:shell 分叉,分叉进程从管道创建所有其他进程,并最终转换为最右边的进程。
最后一个版本需要的代码比其他版本少得多,但速度较慢。由于代码大小的原因,它于 1976 年使用。
第一个变体是最快的变体,但需要比其他变体更多的代码,但它是唯一在原始 shell 进程中运行变量赋值的变体,需要在主 shell 中具有修改后的变量值。