如何在Bash脚本中添加数字?

762

我有这个Bash脚本,在第16行遇到了问题。 如何将第15行的先前结果加入到第16行的变量中?

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        num= $num + $metab   (line16)
    done
    echo "$num"
 done

2
我认为即使使用以下答案,您也会打印零值。有一个技巧,例如进程替换,可以将“内部num”的最终值带到“外部num”。 - Scott Chu
13个回答

1285

对于整数

num=$((num1 + num2))
num=$(($num1 + $num2))       # Also works
num=$((num1 + 2 + 3))        # ...
num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
使用外部的expr工具。请注意,这只适用于非常旧的系统。
num=`expr $num1 + $num2`     # Whitespace for expr is important

对于浮点数:

Bash不直接支持,但有几个外部工具可用:

num=$(awk "BEGIN {print $num1+$num2; exit}")
num=$(python -c "print $num1+$num2")
num=$(perl -e "print $num1+$num2")
num=$(echo $num1 + $num2 | bc)   # Whitespace for echo is important

你也可以使用科学计数法(例如,2.5e+2)。


常见陷阱:

  • 当设置变量时,等号两侧不能有空格,否则它会强制 shell 将第一个单词解释为要运行的应用程序名称(例如,num=num

    num= 1 num =2

  • bcexpr 需要每个数字和操作符作为单独的参数,因此空格很重要。 它们不能处理像 3+ +4 这样的参数。

    num=`expr $num1+ $num2`


1
在第一个答案中,它打印了“expr:非数字参数” 第二个不起作用。 - Nick
1
@Nick:你试过在存在名为num1num2且拥有整数值变量的情况下尝试过这个吗? - sorpigal
1
是的,它们存在,但一个变量是双精度浮点数。这会有问题吗? - Nick
2
是的,这是个问题 :) 注意:$((..)) 算术运算在 bash 中执行。expr 作为一个单独的进程执行,因此速度会慢很多。在不支持算术运算的系统(sh!=bash)上使用后者。 - Karoly Horvath
4
由于 $((…)) 已被 POSIX 标准化,因此使用 expr 的情况应该越来越少。 - chepner
显示剩余4条评论

180

3
奇怪,当我试图计算 191.003 + 190 时,这里不起作用。 - Sigur
2
@Sigur 我认为这是因为bash不支持浮点数运算,也就是说,这种方法虽然很好,但只适用于整数。请尝试使用其中一个dcbc解决方案。 - Hashim Aziz
1
(()) 之间必须使用 $ 吗?好像不需要。 - Manuel Jordan
@ManuelJordan 不,num=$((num1 + num2)) 也可以正常工作。 - Eli

37

有一千零一种方法可以实现它。这里提供一种使用dc的方法(它是一款支持无限精度算术运算的逆波兰式台式计算器):

dc <<<"$num1 $num2 + p"

但如果那对您来说太像 Bash 了(或者需要可移植性),您可以这样说

echo $num1 $num2 + p | dc

但是也许你是那些认为逆波兰表示法很麻烦的人,别担心!bc可以帮助你:

bc <<< "$num1 + $num2"
echo $num1 + $num2 | bc

话虽如此,你的脚本还有一些与此无关的改进空间:

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in output-$i-* ; do # 'for' can glob directly, no need to ls
            echo "$j"

             # 'grep' can read files, no need to use 'cat'
            metab=$(grep EndBuffer "$j" | awk '{sum+=$2} END { print sum/120}')
            num=$(( $num + $metab ))
    done
    echo "$num"
done

Bash FAQ 022所述,Bash 不支持原生浮点数计算。如果需要对浮点数进行求和,则需要使用外部工具(如bcdc)。

在这种情况下,解决方案为:

num=$(dc <<<"$num $metab + p")

将可能是浮点数的数字累加到num中。


1
@Sorpigal 非常感谢.. 我可以再问你一个问题吗?如何在结尾打印num/10而不是num? - Nick

27

在Bash中,

 num=5
 x=6
 (( num += x ))
 echo $num   # ==> 11

请注意,Bash 只能处理整数算术运算,因此如果您的 AWK 命令返回一个分数,则需要重新设计:以下是稍微改写过以在 AWK 中执行所有数学计算的代码。

num=0
for ((i=1; i<=2; i++)); do
    for j in output-$i-*; do
        echo "$j"
        num=$(
           awk -v n="$num" '
               /EndBuffer/ {sum += $2}
               END {print n + (sum/120)}
           ' "$j"
        )
    done
    echo "$num"
done

27
我总是忘记语法,所以我来谷歌搜索,但我从来没有找到我熟悉的那个 :P。这对我来说是最清晰的,也更符合我对其他语言的期望。
i=0
((i++))

echo $i;

20

我也非常喜欢这种方法。它更简洁:

count=$[count+1]

2
顺便问一下,这为什么能够工作?我们该如何称呼它?我找不到关于这些的文档。 - ljk321
6
更少的混乱,但已经不推荐使用了。 - Camille Goudeseune

18
 #!/bin/bash
read X
read Y
echo "$(($X+$Y))"

1
在 macOS 上运行良好。 - Tmx

11

使用shell内置的let。它类似于(( expr ))

A=1
B=1
let "C = $A + $B"
echo $C # C == 2

来源: Bash let内置命令


11

你应该将 metab 声明为整数,然后使用算术运算。

declare -i metab num
...
num+=metab
...

了解更多信息,请查看6.5 Shell Arithmetic


7

另一个在Bash中执行的便携式POSIX兼容方式,可以在.bashrc中定义为函数,以提供所有算术运算符的便利性。

addNumbers () {
    local IFS='+'
    printf "%s\n" "$(( $* ))"
}

并在命令行中调用它,如下所示:

addNumbers 1 2 3 4 5 100
115

这个想法是使用输入字段分隔符(IFS),它是Bash中用于扩展后进行单词拆分并将行拆分为单词的特殊变量。该函数在本地更改值以使用单词拆分字符作为求和运算符+
请记住,IFS在本地更改,不会影响函数范围外的默认IFS行为。来自man bash页面的摘录:
引用:

shell将IFS的每个字符视为分隔符,并在这些字符上将其他扩展的结果拆分成单词。如果IFS未设置或其值正好为默认值,则忽略前一次扩展结果的开头和结尾处的,,和空格序列,并且任何不在开头或结尾处的IFS字符序列都用于分隔单词。

"$(( $* ))"表示要通过+拆分传递的参数列表,稍后使用printf函数输出总和值。该函数可以扩展以添加其他算术运算的范围。

如果您使用Bash,它不符合POSIX标准,因为在没有Bash的POSIX机器上无法工作。 - darkfeline

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接