Shell脚本输入重定向异常
谁能解释这种行为?
跑步:
#!/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壳尝试了相同的代码,并且从它们得到了相同的行为。
解决方案
回答
这是因为管道版本正在创建一个子外壳,该子外壳将变量读入其本地空间,然后在子外壳退出时销毁该变量。
执行此命令
$ echo $$;cat | read a 10637
并使用pstree -p查看正在运行的进程,我们将看到一个额外的shell挂在主shell上。
| |-bash(10637)-+-bash(10786) | | `-cat(10785)
回答
好吧,我知道了!
这是一个很难捕获的错误,但是是由外壳处理管道的方式导致的。管道的每个元素都在单独的过程中运行。当read命令设置var1和var2时,将它们设置为自己的子shell,而不是父shell。因此,当子外壳程序退出时,var1和var2的值将丢失。我们可以尝试做
var1=$(echo "Hello") echo var1
它返回预期的答案。不幸的是,这仅适用于单个变量,我们一次不能设置多个。为了一次设置多个变量,我们必须读入一个变量并将其切成多个变量,或者使用类似以下内容的方法:
set -- $(echo "Hello World") var1="" var2="" echo $var1 echo $var2
虽然我承认它不如使用管道优雅,但它可以工作。当然,我们应该记住,读取是要从文件读取到变量中的,因此使其从标准输入中读取会有点困难。
回答
已经正确回答了此问题,但尚未提出解决方案。使用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是更好的交互式外壳。)
回答
该职位已得到适当答复,但我想提供一种可能有用的替代衬垫。
要将回声(或者标准输出)中的空格分隔值分配给外壳变量,可以考虑使用外壳数组:
$ var=( $( echo 'hello world' ) ) $ echo ${var[0]} hello $ echo ${var[1]} world
在此示例中,var是一个数组,可以使用构造$ {var [index]}访问内容,其中index是数组索引(以0开头)。
这样,我们就可以将任意数量的参数分配给相关的数组索引。
回答
read var1 var2 < <(echo "hello world")
回答
尝试:
echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )
正如多人所言,问题是var1和var2是在子外壳环境中创建的,该环境在该子外壳退出时会被破坏。上面的代码避免了破坏子外壳,直到结果被回显为止。另一个解决方案是:
result=`echo "hello world"` read var1 var2 <<EOF $result EOF echo $var1 echo $var2
回答
#!/bin/sh echo "hello world" | read var1 var2 echo $var1 echo $var2
因为管道在子外壳中运行它们的每个组件,所以不会产生任何输出。子外壳继承父外壳变量的副本,而不是共享它们。试试这个:
#!/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
花括号仅用于分组,不创建子外壳,花括号内部修改的$ foo与花括号外部修改的$ foo相同。
现在尝试这个:
#!/bin/sh echo "hello world" | { read var1 var2 echo $var1 echo $var2 } echo $var1 echo $var2
在花括号内,内置的read会正确创建$ var1和$ var2,我们可以看到它们得到了回显。在大括号之外,它们不再存在。花括号中的所有代码都在子shell中运行,因为它是管道的一个组成部分。
我们可以在花括号之间放置任意数量的代码,因此,每当需要运行用于解析其他内容输出的Shell脚本块时,都可以使用此管道成块结构。
回答
我对这个问题的看法(使用Bash):
read var1 var2 <<< "hello world" echo $var1 $var2