bash:循环的管道输出似乎改变了循环内的范围 - 为什么? [英] bash: piping output from a loop seems to change the scope within the loop - why?

查看:17
本文介绍了bash:循环的管道输出似乎改变了循环内的范围 - 为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到,如果我通过管道传输循环的输出,那么 bash for 循环中的变量范围似乎会发生变化.

I've noticed that variable scope within a bash for loop seems to change if I pipe the output of the loop.

例如,这里g在循环后保持改变:

For example, here g remains changed after the loop:

$ g=bing; for f in foo; do g=fing; echo g in loop: $g; done; echo g after $g;
g in loop: fing
g after fing

而在这里,循环期间的更改被遗忘了:

whereas here, the change during the loop is forgotten:

$ g=bing; for f in foo; do g=fing; echo g in loop: $g; done | cat; echo g after $g;
g in loop: fing
g after bing

管道接收器中g的值也来自外部"上下文:

The value of g in the receiver of the pipe is from the "outer" context too:

$ g=bing; for f in foo; do g=fing; echo g in loop: $g; done | (cat; echo in pipe $g;); echo g after $g;
g in loop: fing
in pipe bing
g after bing

怎么回事?

推荐答案

来自 bash 手册页

From the bash man page

管道中的每个命令都作为一个单独的进程(即在子shell中)执行.

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

这意味着管道的两端都在一个子shell中运行.

This means that both sides of the pipeline are run in a subshell.

来自 http://www.tldp.org/LDP/abs/html/subshel​​ls.html

子shell中的变量在子shell中的代码块之外是不可见的.父进程和启动子 shell 的 shell 无法访问它们.这些实际上是子进程的局部变量.

Variables in a subshell are not visible outside the block of code in the subshell. They are not accessible to the parent process, to the shell that launched the subshell. These are, in effect, variables local to the child process.

这意味着当管道结束时,对变量的所有更改都将丢失.

This means that when the pipeline ends all changes to variables are lost.

这是使用 BASH_SUBSHELL

BASH_SUBSHELL每次产生子外壳或子外壳环境时增加 1.初始值为0.

BASH_SUBSHELL Incremented by one each time a subshell or subshell environment is spawned. The initial value is 0.

输入:

echo "before loop:$BASH_SUBSHELL"
for i in foo; do echo "in loop:$BASH_SUBSHELL"; done | (cat;echo "second pipe: $BASH_SUBSHELL")
echo "out of pipe: $BASH_SUBSHELL"

输出:

before loop:0
in loop:1
second pipe: 1
out of pipe: 0

正如你所看到的,循环内部和管道的第二部分都在子壳内运行,它们在管道的末端结束.

As you can see both inside the loop and the second part of the pipe have been run inside subshells, and they end at the end of the pipeline.

编辑 2

意识到这样做可能更清楚地显示运行的不同子shell

Realised it was probably clearer to do this to show the different subshells that are run

在旧的 bash 中,它不包含 $BASHPID 这实际上是查看子外壳程序 pid 的唯一方法,但是您可以声明一个类似

In old bashes it doesn't include $BASHPID which is really the only way to see the pid of subshells, but you can declare a function like

GetPid(){ cut -d " " -f 4 /proc/self/stat; }

效果几乎一样

echo -n "before loop:";GetPid
for i in foo; do echo -n "in loop:";GetPid; done | (cat;echo -n "second pipe:";GetPid)
echo -n "out of pipe:";GetPid

Bash 4.0+

 echo "before loop:$BASHPID"
 for i in foo; do echo "in loop:$BASHPID"; done | (cat;echo "second pipe: $BASHPID")
 echo "out of pipe: $BASHPID"

输出:

before loop:29985
in loop:12170
second pipe:12171
out of pipe:29985

正如您所看到的,这更清楚地表明,在管道之前和之后,您与原始变量在同一个 shell 中.
您的第三种情况也已解决,因为管道的两侧在不同的子shell 中运行,变量被重置为每个管道命令的父值,因此在循环后会恢复,即使它仍然是相同的管道.

As you can see this makes it clearer that before and after the pipeline you are in the same shell with the original variable.
Your third case is also solved as both sides of the pipe run in different subshells the variable is reset to the parent value for each piped command, so will have reverted back after the loop, even though it is still the same pipeline.

这篇关于bash:循环的管道输出似乎改变了循环内的范围 - 为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆