Bash:使用 tee 的 for 循环中的变量范围

Bash:使用 tee 的 for 循环中的变量范围

考虑:

numbers="1 111 5 23 56 211 63"
max=0

for num in ${numbers[@]}; do

      [ $num -gt $max ]\
           && echo "old -> new max = $max -> $num"\
           && max=$num

done | tee logfile

echo "Max= $max"

如果我删除| tee logfilemax 变量,则会正确打印为 211,但如果我将其保留,则会得到Max= 0.

到底是怎么回事?

答案1

管道的每一侧都在子 shell 中执行。子 shell 是原始 shell 进程的副本,它以相同的状态启动,然后独立演化。子 shell 中设置的变量无法转义回父 shell。

在 bash 中,您可以使用而不是使用管道流程替代。这与管道的行为几乎相同,只是循环在原始 shell 中执行并且仅tee在子 shell 中执行。

for num in "${numbers[@]}"; do
  if ((num > max)); then
    echo "old -> new max = $max -> $num"
    max=$num
  fi
done > >(tee logfile)
echo "Max= $max"

当我在做的时候,我改变了你的脚本中的一些内容:

请注意,管道解决方案和子 shell 解决方案之间存在细微差别:管道命令在两个命令都退出时完成,而具有进程替换的命令在主命令退出时完成,而不等待进程替换中的进程。这意味着当您的脚本完成时,日志文件可能尚未完全写入。

另一种方法是使用其他通道从子 shell 到原始 shell 进程进行通信。你可以使用临时文件(灵活,但做起来更难——将临时文件写入适当的目录,避免名称冲突(使用mktemp),即使出现错误也将其删除)。或者你可以使用另一个管道传达结果并在命令替换中获取结果。

max=$({ { for … done;
          echo "$max" >&4
        } | tee logfile >&3; } 4>&1) 3&>1

的输出tee转到文件描述符 3,该文件描述符被重定向到脚本的标准输出。的输出echo "$max"转到文件描述符 4,该描述符被重定向到命令替换。

答案2

一旦将 for 循环放入管道中,它就会在子 shell 中运行,该子 shell 不会将其变量传递给 supershell。

答案3

它无法在管道中幸存,但这是有效的:

numbers="1 111 5 23 56 211 63"  
max=0  
echo $max>maxfile  

for num in ${numbers[@]}; do  

      [ $num -gt $max ]\  
           && echo "old -> new max = $max -> $num"\  
           && max=$num\  
           && echo $max>maxfile  

done | tee logfile  

read max<maxfile  
echo "Max= $max"  

相关内容