bash Shell 脚本输入重定向异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2732/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Shell scripting input redirection oddities
提问by Ryan Ahearn
Can anyone explain this behavior? Running:
谁能解释这种行为?跑步:
#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2
results in nothing being ouput, while:
结果没有任何输出,而:
#!/bin/sh
echo "hello world" > test.file
read var1 var2 < test.file
echo $var1
echo $var2
produces the expected output:
产生预期的输出:
hello
world
Shouldn't the pipe do in one step what the redirection to test.file did in the second example? I tried the same code with both the dash and bash shells and got the same behavior from both of them.
管道不应该在一个步骤中完成第二个示例中重定向到 test.file 的操作吗?我用 dash 和 bash shell 尝试了相同的代码,并从它们两个那里得到了相同的行为。
采纳答案by chepner
A recent addition to bashis the lastpipeoption, which allows the last command in a pipeline to run in the current shell, not a subshell, when job control is deactivated.
最近添加了bash一个lastpipe选项,当作业控制被停用时,它允许管道中的最后一个命令在当前 shell 中运行,而不是在子 shell 中运行。
#!/bin/bash
set +m # Deactiveate job control
shopt -s lastpipe
echo "hello world" | read var1 var2
echo $var1
echo $var2
will indeed output
确实会输出
hello
world
回答by flabdablet
#!/bin/sh
echo "hello world" | read var1 var2
echo $var1
echo $var2
produces no output because pipelines run each of their components inside a subshell. Subshells inherit copiesof the parent shell's variables, rather than sharing them. Try this:
不产生任何输出,因为管道在子外壳内运行其每个组件。子shell继承拷贝父进程的变量,而不是分享。尝试这个:
#!/bin/sh
foo="contents of shell variable foo"
echo $foo
(
echo $foo
foo="foo contents modified"
echo $foo
)
echo $foo
The parentheses define a region of code that gets run in a subshell, and $foo retains its original value after being modified inside them.
括号定义了在子shell中运行的代码区域,$foo在其中修改后保留其原始值。
Now try this:
现在试试这个:
#!/bin/sh
foo="contents of shell variable foo"
echo $foo
{
echo $foo
foo="foo contents modified"
echo $foo
}
echo $foo
The braces are purely for grouping, no subshell is created, and the $foo modified inside the braces is the same $foo modified outside them.
大括号纯粹是为了分组,没有创建子shell,大括号内修改的 $foo 与括号外修改的 $foo 相同。
Now try this:
现在试试这个:
#!/bin/sh
echo "hello world" | {
read var1 var2
echo $var1
echo $var2
}
echo $var1
echo $var2
Inside the braces, the read builtin creates $var1 and $var2 properly and you can see that they get echoed. Outside the braces, they don't exist any more. All the code within the braces has been run in a subshell because it's one component of a pipeline.
在大括号内, read 内置函数正确地创建了 $var1 和 $var2 ,您可以看到它们得到了回应。在大括号之外,它们不再存在。大括号内的所有代码都在子shell 中运行,因为它是管道的一个组件。
You can put arbitrary amounts of code between braces, so you can use this piping-into-a-block construction whenever you need to run a block of shell script that parses the output of something else.
您可以在大括号之间放置任意数量的代码,因此当您需要运行一个解析其他内容的输出的 shell 脚本块时,您可以使用这种管道到块结构。
回答by Jon Ericson
This has already been answered correctly, but the solution has not been stated yet. Use ksh, not bash. Compare:
这已经得到正确回答,但尚未说明解决方案。使用 ksh,而不是 bash。相比:
$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | bash -s
To:
到:
$ echo 'echo "hello world" | read var1 var2
echo $var1
echo $var2' | ksh -s
hello
world
ksh is a superior programming shell because of little niceties like this. (bash is the better interactive shell, in my opinion.)
由于像这样的小细节,ksh 是一个卓越的编程 shell。(在我看来,bash 是更好的交互式 shell。)
回答by Steve Baker
read var1 var2 < <(echo "hello world")
回答by Steve Baker
The post has been properly answered, but I would like to offer an alternative one liner that perhaps could be of some use.
该帖子已得到正确回答,但我想提供另一种可能有用的衬垫。
For assigning space separated values from echo (or stdout for that matter) to shell variables, you could consider using shell arrays:
要将空格分隔的值从 echo(或标准输出)分配给 shell 变量,您可以考虑使用 shell 数组:
$ var=( $( echo 'hello world' ) )
$ echo ${var[0]}
hello
$ echo ${var[1]}
world
In this example var is an array and the contents can be accessed using the construct ${var[index]}, where index is the array index (starts with 0).
在此示例中,var 是一个数组,可以使用构造 ${var[index]} 访问其内容,其中 index 是数组索引(从 0 开始)。
That way you can have as many parameters as you want assigned to the relevant array index.
这样,您可以将任意数量的参数分配给相关的数组索引。
回答by num1
Allright, I figured it out!
好吧,我想通了!
This is a hard bug to catch, but results from the way pipes are handled by the shell. Every element of a pipeline runs in a separate process. When the read command sets var1 and var2, is sets them it its own subshell, not the parent shell. So when the subshell exits, the values of var1 and var2 are lost. You can, however, try doing
这是一个很难捕捉的错误,但这是由 shell 处理管道的方式造成的。管道的每个元素都在单独的进程中运行。当读取命令设置 var1 和 var2 时,将它们设置为自己的子外壳,而不是父外壳。所以当子shell退出时,var1和var2的值都丢失了。但是,您可以尝试执行
var1=$(echo "Hello")
echo var1
which returns the expected answer. Unfortunately this only works for single variables, you can't set many at a time. In order to set multiple variables at a time you must either read into one variable and chop it up into multiple variables or use something like this:
它返回预期的答案。不幸的是,这只适用于单个变量,您不能一次设置多个变量。为了一次设置多个变量,您必须读入一个变量并将其分成多个变量,或者使用以下内容:
set -- $(echo "Hello World")
var1="" var2=""
echo $var1
echo $var2
While I admit it's not as elegant as using a pipe, it works. Of course you should keep in mind that read was meant to read from files into variables, so making it read from standard input should be a little harder.
虽然我承认它不像使用管道那么优雅,但它确实有效。当然你应该记住 read 是为了从文件读取到变量,所以让它从标准输入读取应该有点困难。
回答by Mark Harrison
It's because the pipe version is creating a subshell, which reads the variable into its local space which then is destroyed when the subshell exits.
这是因为管道版本正在创建一个子外壳,它将变量读入其本地空间,然后在子外壳退出时将其销毁。
Execute this command
执行这个命令
$ echo $$;cat | read a
10637
and use pstree -p to look at the running processes, you will see an extra shell hanging off of your main shell.
并使用 pstree -p 查看正在运行的进程,你会看到一个额外的 shell 挂在你的主 shell 上。
| |-bash(10637)-+-bash(10786)
| | `-cat(10785)
回答by Mark Harrison
My take on this issue (using Bash):
我对这个问题的看法(使用 Bash):
read var1 var2 <<< "hello world"
echo $var1 $var2
回答by pjz
Try:
尝试:
echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )
The problem, as multiple people have stated, is that var1 and var2 are created in a subshell environment that is destroyed when that subshell exits. The above avoids destroying the subshell until the result has been echo'd. Another solution is:
正如许多人所说,问题在于 var1 和 var2 是在子 shell 环境中创建的,当该子 shell 退出时该环境会被破坏。以上避免了在结果被回显之前破坏子shell。另一种解决方案是:
result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2

