bash 为什么管道输入到“读取”仅在输入“同时读取...”构造时才有效?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13763942/
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
Why piping input to "read" only works when fed into "while read ..." construct?
提问by huoneusto
I've been trying to read input into environment variables from program output like this:
我一直在尝试从程序输出中读取输入到环境变量中,如下所示:
echo first second | read A B ; echo $A-$B
And the result is:
结果是:
-
Both A and B are always empty. I read about bash executing piped commands in sub-shell and that basically preventing one from piping input to read. However, the following:
A 和 B 始终为空。我读到 bash 在子 shell 中执行管道命令,这基本上阻止了一个管道输入读取。但是,以下内容:
echo first second | while read A B ; do echo $A-$B ; done
Seems to work, the result is:
似乎有效,结果是:
first-second
Can someone please explain what is the logic here? Is it that the commands inside the while
... done
construct are actually executed in the same shell as echo
and not in a sub-shell?
有人可以解释一下这里的逻辑是什么吗?while
...done
构造中的命令实际上是在与echo
子 shell相同的 shell 中执行的,而不是在子 shell 中执行的吗?
采纳答案by F. Hauri
How to do a loop against stdinand get result stored in a variable
如何对stdin执行循环并将结果存储在变量中
Under bash(and other shellalso), when you pipe something by using |
to another command, you will implicitely create a fork, a subshell who's a child of current session and who can't affect current session's environ.
在bash(以及其他shell)下,当您通过使用管道将某些东西|
传递给另一个命令时,您将隐式地创建一个fork,一个作为当前会话的子级并且不能影响当前会话环境的子 shell。
So this:
所以这:
TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done
echo final total: $TOTAL
won't give expected result! :
不会给出预期的结果!:
9 - 4 = 5 -> TOTAL= 5
3 - 1 = 2 -> TOTAL= 7
77 - 2 = 75 -> TOTAL= 82
25 - 12 = 13 -> TOTAL= 95
226 - 664 = -438 -> TOTAL= -343
echo final total: $TOTAL
final total: 0
Where computed TOTALcould'nt be reused in main script.
无法在主脚本中重用计算出的TOTAL 的地方。
Inverting the fork
倒叉
By using bashProcess Substitution, Here Documentsor Here Strings, you could inverse the fork:
通过使用bash Process Substitution、Here Documents或Here Strings,您可以反转分叉:
Here strings
这里的字符串
read A B <<<"first second"
echo $A
first
echo $B
second
Here Documents
这里的文件
while read A B;do
echo $A-$B
C=$A-$B
done << eodoc
first second
third fourth
eodoc
first-second
third-fourth
outside of the loop:
循环之外:
echo : $C
: third-fourth
Here Commands
这里命令
TOTAL=0
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done < <(
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664
)
9 - 4 = 5 -> TOTAL= 5
3 - 1 = 2 -> TOTAL= 7
77 - 2 = 75 -> TOTAL= 82
25 - 12 = 13 -> TOTAL= 95
226 - 664 = -438 -> TOTAL= -343
# and finally out of loop:
echo $TOTAL
-343
Now you could use $TOTAL
in your main script.
现在您可以$TOTAL
在主脚本中使用。
Piping to a command list
管道到命令列表
But for working only against stdin, you may create a kind of script into the fork:
但是为了仅针对stdin工作,您可以在fork 中创建一种脚本:
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 | {
TOTAL=0
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done
echo "Out of the loop total:" $TOTAL
}
Will give:
会给:
9 - 4 = 5 -> TOTAL= 5
3 - 1 = 2 -> TOTAL= 7
77 - 2 = 75 -> TOTAL= 82
25 - 12 = 13 -> TOTAL= 95
226 - 664 = -438 -> TOTAL= -343
Out of the loop total: -343
Note: $TOTAL
could not be used in main script(after last right curly bracket }
).
注意:$TOTAL
不能在主脚本中使用(在最后一个右大括号之后}
)。
Using lastpipebash option
使用lastpipebash 选项
As @CharlesDuffy correctly pointed out, there is a bash option used to change this behaviour. But for this, we have to first disablejob control:
正如@CharlesDuffy 正确指出的那样,有一个 bash 选项用于改变这种行为。但为此,我们必须首先禁用作业控制:
shopt -s lastpipe # Set *lastpipe* option
set +m # Disabling job control
TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
while read A B;do
((TOTAL+=A-B))
printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
done
9 - 4 = 5 -> TOTAL= -338
3 - 1 = 2 -> TOTAL= -336
77 - 2 = 75 -> TOTAL= -261
25 - 12 = 13 -> TOTAL= -248
226 - 664 = -438 -> TOTAL= -686
echo final total: $TOTAL
-343
This will work, but I (personally) don't like this because this is not standardand won't help to make script readable. Also disabling job control seem expensive for accessing this behaviour.
这会起作用,但我(个人)不喜欢这样,因为这不是标准的并且无助于使脚本可读。同样禁用作业控制对于访问此行为来说似乎很昂贵。
Note:Job controlis enabled by default only in interactivesessions. So set +m
is not required in normal scripts.
注意:默认情况下仅在交互式会话中启用作业控制。所以在普通脚本中不需要。set +m
So forgotten set +m
in a script would create different behaviours if run in a console or if run in a script. This will not going to make this easy to understand or to debug...
因此set +m
,如果在控制台中运行或在脚本中运行,则在脚本中遗忘会产生不同的行为。这不会使它易于理解或调试......
回答by pbhd
First, this pipe-chain is executed:
首先,执行这个管道链:
echo first second | read A B
then
然后
echo $A-$B
Because the read A B
is executed in a subshell, A and B are lost.
If you do this:
因为read A B
在子shell中执行,A和B都丢失了。如果你这样做:
echo first second | (read A B ; echo $A-$B)
then both read A B
and echo $A-$B
are executed in the same subshell (see manpage of bash, search for (list)
然后既read A B
与echo $A-$B
在相同的子shell被执行(见的bash手册页,搜索(list)
回答by immotus
a much cleaner work-around...
一个更清洁的解决方法......
read -r a b < <(echo "$first $second")
echo "$a $b"
This way, read isn't executed in a sub-shell (which would clear the variables as soon as that sub-shell has ended). Instead, the variables you want to use are echoed in a sub-shell that automatically inherits the variables from the parent shell.
这样, read 不会在子 shell 中执行(它会在子 shell 结束后立即清除变量)。相反,您要使用的变量会在子 shell 中回显,该子 shell 会自动从父 shell 继承变量。
回答by Martin Kealey
What you are seeing is the separation between processes: the read
occurs in a subshell - a separate process which cannot alter the variables in the main process (where echo
commands later occur).
您所看到的是进程之间的分离:read
发生在子外壳中 - 一个单独的进程,它不能改变主进程中的变量(echo
命令稍后发生)。
A pipeline (like A | B
) implicitly places each component in a sub-shell (a separate process), even for built-ins (like read
) that usually run in the context of the shell (in the same process).
管道(如A | B
)隐式地将每个组件放置在一个子外壳(一个单独的进程)中,即使对于read
通常在外壳的上下文中(在同一进程中)运行的内置函数(如)也是如此。
The difference in the case of "piping into while" is an illusion. The same rule applies there: the loop is the second half of a pipeline, so it's is in a subshell, but the wholeloop is in the samesubshell, so the separation of processes does not apply.
“piping into while”的差异是一种错觉。同样的规则适用于那里:循环是管道的后半部分,所以它在一个子壳中,但整个循环在同一个子壳中,所以进程的分离不适用。