如何将输入管道传递到 Bash while 循环并在循环结束后保留变量

How to pipe input to a Bash while loop and preserve variables after loop ends

提问人:Wakan Tanka 提问时间:10/24/2013 最后编辑:codeforesterWakan Tanka 更新时间:8/1/2023 访问量:132448

问:

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 循环并返回变量){}i

bash while-loop stdin 管道

评论

0赞 tripleee 9/22/2014
mywiki.wooledge.org/BashFAQ/024
0赞 ivan_pozdeev 6/22/2017
相关新闻: stackoverflow.com/questions/37229058/... .解释所有选项,包括下面提到的工艺替代及其优缺点。lastpipe
1赞 codeforester 8/2/2017
相关:在 while 循环中修改的变量不会被记住
0赞 F. Hauri - Give Up GitHub 6/13/2022
@codeforester还有更多的骗子......相关:为什么将“读取”的输入管道传输到“读取”中时才有效构建?从管道中将值读入 shell 变量

答:

160赞 Jonathan Leffler 10/25/2013 #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 环境中未在后台执行的管道的最后一个命令。

因此,在脚本中使用类似这样的东西可以使修改后的 Modfied 在循环后可用: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 在他的回答中指出的那样,您有时可以使用 here 字符串

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

这不需要 ;您可以使用它来保存进程。shopt

评论

2赞 Wakan Tanka 2/23/2015
请原谅我的无知。我知道这是正确的解决方案,我已将其标记为答案,因此它对我有用。但是现在当我运行它两次时,它没有返回最后一行。我做错了什么?while read i; do echo $i; done < <(cat /etc/passwd); echo $i
0赞 Jonathan Leffler 2/23/2015
@WakanTanka:我不得不尝试一下......我相信答案是失败的读取重置为空,因此循环后的回声回显空行。i
1赞 Atcold 9/20/2016
感谢 Process Substitution 参考。我不知道。
0赞 yurenchen 11/19/2018
@Wakan Tanka:得到了与你相同的结果,我使用工作,//也许那些日子 bash 行为发生了变化?while read i; do x=$i; done < <(cat /etc/passwd); echo i=$i; echo x=$x
0赞 Pat 11/6/2019
你是救命恩人!一直在寻找类似的解决方案。你的是唯一有效的。
33赞 Gareth Rees 10/25/2013 #2

Jonathan Leffler 解释了如何使用进程替换来做你想做的事,但另一种可能性是使用 here 字符串

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

这样可以节省一个过程。

1赞 Steve 8/2/2020 #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
}
0赞 Alberto Salvia Novella 6/1/2023 #4

最快的是:

while read -r ITEM; do
    OPERATION
done < <(COMMAND)

或者用一行:

while read -r ITEM; do OPERATION; done < <(COMMAND)
0赞 ocodo 8/1/2023 #5

Bash 提供 var 默认行为,因此:

while read -r line
do
  total=${total:-0}
  total=$((total + $(calculation code)))
done
echo $total

这允许循环轻松捕获脚本和函数的 stdin。