谁能解释这种行为?跑步:

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

结果什么也没有输出,同时:

#!/bin/sh
echo "hello world" > test.file
read var1 var2 < test.file
echo $var1
echo $var2

产生预期的输出:

hello
world

管道不应该一步完成第二个示例中重定向到 test.file 所做的事情吗?我在 dash 和 bash shell 中尝试了相同的代码,并从它们中得到了相同的行为。

有帮助吗?

解决方案

最近添加了 bash 是个 lastpipe 选项,当作业控制停用时,它允许管道中的最后一个命令在当前 shell 中运行,而不是在子 shell 中运行。

#!/bin/bash
set +m      # Deactiveate job control
shopt -s lastpipe
echo "hello world" | read var1 var2
echo $var1
echo $var2

确实会输出

hello
world

其他提示

#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2

不产生输出,因为管道在子 shell 内运行其每个组件。子壳 继承副本 父 shell 的变量,而不是共享它们。尝试这个:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
(
    echo $foo
    foo="foo contents modified"
    echo $foo
)
echo $foo

括号定义了在子 shell 中运行的代码区域,$foo 在其中修改后保留其原始值。

现在试试这个:

#!/bin/sh
foo="contents of shell variable foo"
echo $foo
{
    echo $foo
    foo="foo contents modified"
    echo $foo
}
echo $foo

大括号纯粹用于分组,不会创建子 shell,大括号内修改的 $foo 与大括号外修改的 $foo 相同。

现在试试这个:

#!/bin/sh
echo "hello world" | {
    read var1 var2
    echo $var1
    echo $var2
}
echo $var1
echo $var2

在大括号内, read 内置函数正确创建 $var1 和 $var2 ,您可以看到它们得到了回显。在大括号之外,它们不再存在。大括号内的所有代码都已在子shell中运行 因为它是管道的一个组成部分.

您可以在大括号之间放置任意数量的代码,因此每当您需要运行解析其他内容的输出的 shell 脚本块时,都可以使用这种管道插入块结构。

这已经被正确回答了,但解决方案尚未说明。使用 ksh,而不是 bash。比较:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | bash -s

到:

$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | ksh -s
hello
world

ksh 是一个优秀的编程 shell,因为有这样的小细节。(在我看来,bash 是更好的交互式 shell。)

read var1 var2 < <(echo "hello world")

该帖子已得到正确答复,但我想提供另一种可能有用的衬里。

要将来自 echo(或 stdout)的空格分隔值分配给 shell 变量,您可以考虑使用 shell 数组:

$ var=( $( echo 'hello world' ) )
$ echo ${var[0]}
hello
$ echo ${var[1]}
world

在此示例中,var 是一个数组,可以使用构造 ${var[index]} 访问内容,其中 index 是数组索引(从 0 开始)。

这样,您就可以将任意数量的参数分配给相关的数组索引。

我对这个问题的看法(使用 Bash):

read var1 var2 <<< "hello world"
echo $var1 $var2

好吧,我想通了!

这是一个很难捕获的错误,但这是由 shell 处理管道的方式造成的。管道的每个元素都在单独的进程中运行。当 read 命令设置 var1 和 var2 时,会将它们设置为其自己的子 shell,而不是父 shell。所以当子shell退出时,var1和var2的值就会丢失。但是,您可以尝试这样做

var1=$(echo "Hello")
echo var1

返回预期的答案。不幸的是,这仅适用于单个变量,您不能一次设置多个变量。为了一次设置多个变量,您必须读入一个变量并将其分成多个变量,或者使用如下所示的内容:

set -- $(echo "Hello World")
var1="$1" var2="$2"
echo $var1
echo $var2

虽然我承认它不如使用管道那么优雅,但它确实有效。当然,您应该记住,读取的目的是从文件读取到变量中,因此从标准输入读取它应该有点困难。

这是因为管道版本正在创建一个子 shell,它将变量读入其本地空间,然后在子 shell 退出时销毁该变量。

执行这个命令

$ echo $$;cat | read a
10637

并使用 pstree -p 查看正在运行的进程,您将看到一个额外的 shell 挂在主 shell 上。

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)

尝试:

echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )

正如许多人所说,问题在于 var1 和 var2 是在子 shell 环境中创建的,当该子 shell 退出时,该环境就会被销毁。上面的方法避免了在结果被回显之前破坏子 shell。另一个解决方案是:

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top