奇怪的bash范围规则使我望而却步

时间:2011-04-18 12:04:20

标签: bash sum

考虑:

t=0 ; for i in 1 2 3 4 5 6 7 8 9 10 ; do t=$((t+i)) ; done ; echo $t

打印55。

可是:

totsize=0
find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du | \
while read size rest ; do
    totsize=$((totsize+size))
    echo "$totsize"
done
echo "Sum: $totsize kb"

打印“Sum:0 kb”,即使临时打印语句打印出合理的总和。

我知道我以前遇到过这个问题,但从未理解过。有什么区别?

4 个答案:

答案 0 :(得分:8)

totsize=0

while read size rest ; do
    totsize=$((totsize+size))
    echo "$totsize"
done < <(find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du)
echo "Sum: $totsize kb"

防止子shell,因为子shell会限制totsize

的范围

再花几句话:

do_something < <(subprocess)
  • 将在主shell中运行do_something(这是输入重定向,其中进程替换

subprocess | do_something
  • 将在单独的(子)shell中运行do_something(这是一个管道子进程)

答案 1 :(得分:6)

那是因为管道创建了一个子shell,所以totsize在子shell中是“本地的”。你可以试试这个(bash)

totsize=0
while read size rest ; do
    totsize=$((totsize+size))
    echo "$totsize"
done < <(find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du)
echo "Sum: $totsize kb"

或者不使用bash,请致电awk

$> find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du | awk  '{s+=$1}END{print "total size: "s}'

但是您确定只想使用du而没有任何选项,因为尺寸不是“准确”(使用du -b会更好)。如果您有GNU查找,则可以使用-printf

find /home/user -type f -mmin -4860 -a -mmin +3420 -printf "%s\n" | awk  '{s+=$1}END{print "total size: "s" bytes"}'

答案 2 :(得分:1)

这是一个常见问题。管道的右侧元素在子shell中运行。在这种情况下,xargswhile都将在shell的子进程中运行(它们都是并行运行的,因此它们必须是单独的进程)。

您需要重构代码,这样才不会在子shell中累积值。其他人已经展示了bash重构。这是使用awk的方法:

find /home/user -type f -mmin -4860 -a -mmin +3420 | xargs du \
| awk '{sum += $1} END {print sum}'

如果要捕获变量中的总和,请将其置于shell函数中:

sum_usage() { ...above pipeline... ; }
totsize=$(sum_usage)

你可以将它全部放在$(...)中,但我认为使用shell函数更容易阅读。

答案 3 :(得分:0)

可能是因为创建了一些子shell(请参阅man bash,搜索命令执行环境)。

但你可以在没有的时候做到:

find . -type f -mmin -4860 -a -mmin +3420 | xargs du | awk '{sum=sum+$1} END {print "Total: " sum " kb."}'

HTH