Shell脚本inputredirect怪异

任何人都可以解释此行为? 运行:

#!/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 

在第二个例子中,pipe道不应该在一步中做什么redirect到test.file? 我用同样的代码与破折号和bash炮弹,并从他们得到相同的行为。

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

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

将确实输出

 hello world 

这已经被正确回答了,但是解决方案还没有被陈述。 使用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是一个优秀的编程外壳,因为这样的小细节。 (在我看来,bash是更好的交互式shell。)

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

不会产生输出,因为管道在子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 

在大括号内部,内置的读内容正确地创建$ var1和$ var2,你可以看到它们被回显。 在大括号之外,他们不再存在了。 所有大括号内的代码已经在子shell中运行, 因为它是管道的一个组件

你可以在花括号之间插入任意数量的代码,所以当你需要运行一个shell脚本来分析其他东西的输出时,你可以使用这种管道式的块结构。

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

好的,我明白了!

这是一个难以捉摸的错误,但是结果来自于shell处理管道的方式。 管道的每个元素都在一个单独的进程中运行。 当读命令设置var1和var2时,将它设置为它自己的子shell,而不是父shell。 所以当子shell退出时,var1和var2的值就会丢失。 但是,你可以尝试做

 var1=$(echo "Hello") echo var1 

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

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

虽然我承认它不像使用管道那样优雅,但它的工作原理。 当然,你应该记住,读取是为了从文件读入变量,所以从标准输入读取应该有点困难。

我在这个问题上(使用Bash):

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

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

执行这个命令

 $ echo $$;cat | read a 10637 

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

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

这个帖子已经得到了很好的回答,但是我想提供一个可能有用的替代版本。

为了从echo(或stdout)分配空间分隔值到shell变量,你可以考虑使用shell数组:

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

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

这样,你可以有任意多的参数,你想分配给相关的数组索引。

尝试:

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

正如多人所说,问题在于var1和var2是在子shell环境中创建的,当子shell退出时会被销毁。 上面的内容避免了子shell的破坏,直到结果被echo'd。 另一个解决方案是

 result=`echo "hello world"` read var1 var2 <<EOF $result EOF echo $var1 echo $var2