【问题标题】:Why is this For loop not not the same as this While loop in Bash?为什么这个 For 循环与 Bash 中的这个 While 循环不同?
【发布时间】:2012-01-17 03:34:41
【问题描述】:

好的,所以我正在尝试解析一个文件,该文件在行中输出我需要的内容,但我无法让这个while 循环工作,我真的很难过。

有趣的是,用for 循环做几乎完全相同的事情确实 工作。有人可以解释一下这里发生了什么吗?

这...

e=""
for f in 1 2 3
do 
    echo $f 
    e="$e.$f"
done
echo $e

输出:

1
2
3
.1.2.3

但这...

e=""
echo "1
2
3" | while read f
do 
    echo $f 
    e="$e.$f"
done
echo $e

输出:

1
2
3

当它们到达e="$e.$f" 时,显然两个循环在$f 中都有1、2 或3,那么第二个循环不起作用是怎么回事?

【问题讨论】:

    标签: bash variables loops


    【解决方案1】:

    我无法告诉你我不得不从某人的代码中提取这个错误多少次。

    基本上管道会生成一个分叉的进程。 e 在分叉进程中设置,但是当它终止时(在 wile 循环结束时),父进程不知道 e 已更改。简而言之,永远不要在涉及管道的命令中设置变量。管道完成时不会设置它。更短...如果可以避免,请不要使用管道。

    e=''
    while read f
    do
      echo $f
      e="$e.$f"
    done << EOF
    1
    2
    3
    EOF
    echo $e
    

    【讨论】:

    • 太棒了,谢谢!我的第二个问题是“我该如何解决?”
    【解决方案2】:

    pipe 将在子 shell 中执行。所以在管道命令执行后变量 e 没有改变。

    【讨论】:

    • 是的,即使你在while循环中导出,var也不能被“推送”到父shell。
    【解决方案3】:

    由于while read... done 是管道的一部分,它拥有自己的分叉进程映像,就像其他二进制文件一样(称为subshel​​l)。 (令人困惑的部分是它是用 shell 编写的,但它并没有作为当前 shell 进程的一部分执行。)这就是变量不会得到更新的原因。

    作为一种解决方法,您总是可以这样做(使用 bashisms):

    while read line
    do
        blablabla
    done < <(echo "1 2 3")
    

    在这里,您避免设置管道,因此while...done shell 代码作为当前脚本的一部分执行。

    【讨论】:

    • +1 向我展示了一个新的符号。我以前从未见过&lt;()
    • 这叫进程替换,移植性不太好(但可以通过创建临时文件来模拟)
    【解决方案4】:

    我猜这是变量范围的差异。当您将 echo 语句通过管道传递到 while 语句时,全局 e 不在其范围内,因此它会打印 f 并设置一个新变量 e。当 while 循环退出时,全局 e 没有改变。因此,您的声明 echo $e 什么也不打印。

    【讨论】:

    • 管道有自己的本地范围? 0_o
    • 这有点像 echo 是一个分叉的进程并且什么都不返回。它是一个子进程并获得了一个新的范围。如果您可以将 e 作为对命令的引用传递,它将更新全局可访问的 e。
    猜你喜欢
    • 1970-01-01
    • 2018-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-22
    相关资源
    最近更新 更多