破折号:从管道读取变量

破折号:从管道读取变量

bashor中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是:

  1. 这里的字符串(适用于 bash、ksh、zsh):

    $ bash -c 'read X Y <<<"AAA BBB"; echo "<$X>" '
    <AAA>
    
  2. 进程替换(适用于 bash、ksh、zsh):

    $ bash -c 'read X Y < <(echo AAA BBB) ; echo "<$X>" '
    <AAA>
    

另一种打印值并在 dash 中工作的替代方法是:

  1. 群组命令:

    $ 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 中具有修改后的变量值。

相关内容