【问题标题】:How to pipe input to a Bash while loop and preserve variables after loop ends如何在循环结束后将输入传送到 Bash while 循环并保留变量
【发布时间】:2013-11-03 10:11:54
【问题描述】:

Bash 允许使用:cat <(echo "$FILECONTENT")

Bash 也允许使用:while read i; do echo $i; done </etc/passwd

结合前两个可以使用:echo $FILECONTENT | while read i; do echo $i; done

最后一个的问题是它创建了子shell,在while循环结束后变量i不能再访问了。

我的问题是:

如何实现这样的事情:while read i; do echo $i; done <(echo "$FILECONTENT") 或者换句话说:我如何确定i 在 while 循环中存活?

请注意,我知道将 while 语句包含在 {} 中,但这并不能解决问题(假设您想在函数中使用 while 循环并返回 i 变量)

【问题讨论】:

标签: bash while-loop stdin pipe


【解决方案1】:

Process Substitution 的正确表示法是:

while read i; do echo $i; done < <(echo "$FILECONTENT")

循环中分配的i 的最后一个值在循环终止时可用。 另一种选择是:

echo $FILECONTENT | 
{
while read i; do echo $i; done
...do other things using $i here...
}

大括号是 I/O 分组操作,本身不会创建子 shell。在这种情况下,它们是管道的一部分,因此作为子shell 运行,但这是因为|,而不是{ ... }。你在问题中提到了这一点。 AFAIK,您可以在函数中从这些函数中返回。


Bash 还提供了 shopt 内置函数,其众多选项之一是:

lastpipe

如果设置,并且作业控制未激活,则 shell 将在当前 shell 环境中运行未在后台执行的管道的最后一个命令。

因此,在脚本中使用类似的东西使得修改后的sum在循环后可用:

FILECONTENT="12 Name
13 Number
14 Information"
shopt -s lastpipe   # Comment this out to see the alternative behaviour
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

在命令行执行此操作通常会遇到“作业控制未激活”(即,在命令行,作业控制已激活)。在不使用脚本的情况下测试失败。

另外,正如Gareth Rees 在他的answer 中所指出的,您有时可以使用here string

while read i; do echo $i; done <<< "$FILECONTENT"

这不需要shopt;您也许可以使用它保存一个进程。

【讨论】:

  • 原谅我的无知。我知道这是正确的解决方案,并且我已将其标记为答案,因此它对我有用。但是现在当我运行while read i; do echo $i; done &lt; &lt;(cat /etc/passwd); echo $i 时,它没有两次返回最后一行。我做错了什么?
  • @WakanTanka:我不得不尝试一下......我相信答案是失败的读取将i 重置为空,因此循环后的回显会回显一个空行。
  • 对流程替换参考的赞誉。我不知道。
  • @Wakan Tanka:得到了和你一样的结果,我用 while read i; do x=$i; done &lt; &lt;(cat /etc/passwd); echo i=$i; echo x=$x 工作,//也许那些日子 bash 行为改变了?
  • 你是救生员!一直在寻找类似的解决方案。你是唯一有效的。
【解决方案2】:

Jonathan Leffler explains 如何使用process substitution 做你想做的事,但另一种可能是使用here string

while read i; do echo "$i"; done <<<"$FILECONTENT"

这节省了一个进程。

【讨论】:

    【解决方案3】:

    这个函数使 jpg 文件重复 $NUM 次(bash)

    function makeDups() {
    NUM=$1
    echo "Making $1 duplicates for $(ls -1 *.jpg|wc -l) files"
    ls -1 *.jpg|sort|while read f
    do
      COUNT=0
      while [ "$COUNT" -le "$NUM" ]
      do
        cp $f ${f//sm/${COUNT}sm}
        ((COUNT++))
      done
    done
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-12
      • 1970-01-01
      相关资源
      最近更新 更多