任何人都可以解释这种行为吗? 运行:
#!/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的操作吗?我用破折号和bash shell尝试了相同的代码,并从两个代码中获得了相同的行为。
答案 0 :(得分:11)
#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2
不产生输出,因为管道在子shell中运行它们的每个组件。子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 builtin正确创建$ var1和$ var2,你可以看到它们被回显。在牙箍之外,它们不再存在。大括号内的所有代码都在子shell 中运行,因为它是管道的一个组件。
你可以在大括号之间放置任意数量的代码,这样你就可以在需要运行一个shell脚本块来解析其他东西的输出时使用这种block-into-a-block结构。
答案 1 :(得分:11)
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
答案 2 :(得分:10)
这已经得到了正确回答,但尚未说明解决方案。使用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。)
答案 3 :(得分:8)
read var1 var2 < <(echo "hello world")
答案 4 :(得分:5)
这是一个难以捕获的错误,但是由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
虽然我承认它并不像使用烟斗那么优雅,但它确实有效。当然,你应该记住,读取是为了从文件读入变量,所以从标准输入读取它应该有点困难。
答案 5 :(得分:5)
该帖子已得到妥善回答,但我想提供另一种可能有用的替代衬垫。
要将空间分隔值从echo(或stdout)分配给shell变量,可以考虑使用shell数组:
$ var=( $( echo 'hello world' ) )
$ echo ${var[0]}
hello
$ echo ${var[1]}
world
在这个例子中,var是一个数组,可以使用构造$ {var [index]}访问内容,其中index是数组索引(从0开始)。
通过这种方式,您可以将所需的参数分配给相关的数组索引。
答案 6 :(得分:5)
我对这个问题的看法(使用Bash):
read var1 var2 <<< "hello world"
echo $var1 $var2
答案 7 :(得分:4)
这是因为管道版本正在创建一个子shell,它将变量读入其本地空间,然后在子shell退出时将其销毁。
执行此命令
$ echo $$;cat | read a
10637
并使用pstree -p查看正在运行的进程,您将看到一个额外的shell悬挂在主shell上。
| |-bash(10637)-+-bash(10786)
| | `-cat(10785)
答案 8 :(得分:3)
尝试:
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