bash - 对变量中的数字求和

bash - 对变量中的数字求和

我有一个变量集,其中的数字用空格分隔,其中第一个数字也可以由空格引导,例如:

VAR=" 2 1  34 3    2 "

我需要将所有这些数字相加。最简单的方法是将数字之间的所有空格替换为+bc 上的管道

我可以用 for 循环、粘贴和 bc 来完成,但也许有人知道更简单的方法?也许直接在 bash 中VAR使用 bash 内置字符串替换进行一些计算?

$ for i in $VAR;do echo $i;done|paste -sd+|bc
42

更新:感谢所有建议,我终于找到了带有数组的相当短的方法:

$ VAR=" 2 1  34 3    2 "
$ arr=( $VAR );echo "$((${arr[@]/%/+}0))"
42
$ VAR="$VAR -14"
$ arr=( $VAR );echo "$((${arr[@]/%/+}0))"
28
$

答案1

您的源字符串包含重复空格和前导/尾随空格。

将 space 简单转换为+将会失败:

$ value='      2 1  34 3    2    '
$ echo "${value// /+}"
++++++2+1++34+3++++2++++

折叠所有重复的空格删除前导/尾随空格,您只需要对未加引号的变量进行 echo(或 printf)(假设 IFS 是默认值):

value=$(echo $value)
echo "${value// /+}"
2+1+34+3+2

这可以提供给 bc:

$ echo "${value// /+}" | bc
42

如果你想要的话,一切都在一行中:

value=$(echo $value); echo "${value// /+}" | bc

或者,甚至用作sed过滤器(没有附加变量,但速度较慢):

echo $value | sed 's/ /+/g' | bc

之前的尝试<<<遇到了一个问题:

$ ~/bin/b44sh -c 'value="      2 1  34 3    2    ";sed "s/ /+/g" <<<$value'
++++++2+1++34+3++++2++++

从 4.4 版开始就在 bash 上。在以前的版本中,它的工作原理如下:

~/bin/b43sh -c 'value="      2 1  34 3    2    ";sed "s/ /+/g" <<<$value'
2+1+34+3+2

对于任何版本的 bash (和 sed),我们都可以这样做(一个相当强大的版本,但调用外部实用程序 - sed):

sed "s/ \+/+/g" <<<"0 $value 0"     | tee /dev/tty     | bc
0+2+1+34+3+2+0
42

纯 shell 解决方案(需要 bash、ksh 或 zsh 作为替换${//}部分)可能是:

value=$(echo $value); bc <<<"${value// /+}

还有一个更多强壮的(执行你的假设™)和便携的版本:

  • 避免 IFS 的更改影响当前 shell。使用子外壳(…)
  • 确保 IFS 在空格上中断值 (IFS=" ")
  • 确保即使不加引号,字符串也不会扩展*等(set -f)。
  • +确保零件用(IFS=+)连接。

( IFS=" "; set -f; set -- $value; IFS=+; echo "$*" | bc; )

函数版本
1 - 如果您的 shell 不允许local,请使用较慢的子 shell 形式
2 - 有些(在 POSIX 中正确)可能会抱怨使用不带引号的$*.

sum(){ local IFS=" "; set -f; set -- $*; IFS=+; echo "$*" | bc; }

它以多种方式添加参数

$ value="      2 1  34 3    2    "
$ sum "$value"
42
$ sum $value    # beware of glob chars *, ? and [  and of odd IFS=123 settings
42
$ sum "      2 1  34 3    2    "
42
$ sum "      2"    "1  "    "34 3"    "    2    "
42
$ var=23
$ sum "      2"    "1  "    "34 3"    "    2    "   "$var"
65

答案2

使用参数扩展。

#!/bin/bash
VAR=" 2 1  34 3    2 "

shopt -s extglob                # Enable the `+(...)` construct.
expression=${VAR#+(\ )}         # Remove leading spaces.
expression=${expression%+(\ )}  # Remove trailing spaces.
bc <<< ${expression//+(\ )/+}   # Replace strings of spaces by pluses.

答案3

一些 Perl 技巧:

$ perl -lane '$t+=$_ for @F; print $t' <<<"$var"
42

或者

$ perl -pe 's/(\d)\s+(?=\d)/$1+/g' <<<"$var" | bc
42

或者

$ perl -lane 'print eval join "+", @F' <<<"$var"
42

或者 GNUsed和 coreutils:

$ tr -s ' ' '+' <<<$var | sed 's/^+//; s/+$//' | bc
42

答案4

bash 脚本:

$ v="2 4 7 10 3"
$ s=0
$ for i in $v
> do
> s=$((s+i))
> done
$ echo $s
26

相关内容