bash - 对变量中的数字求和

Chr*_*ris 4 bash arithmetic bc variable

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

VAR=" 2 1  34 3    2 "
Run Code Online (Sandbox Code Playgroud)

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

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

$ for i in $VAR;do echo $i;done|paste -sd+|bc
42
Run Code Online (Sandbox Code Playgroud)

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

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

小智 7

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

\n\n

将空格简单地转换为+将会失败:

\n\n
$ value=\'      2 1  34 3    2    \'\n$ echo "${value// /+}"\n++++++2+1++34+3++++2++++\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
value=$(echo $value)\necho "${value// /+}"\n2+1+34+3+2\n
Run Code Online (Sandbox Code Playgroud)\n\n

这可以提供给 bc:

\n\n
$ echo "${value// /+}" | bc\n42\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n
$ ~/bin/b44sh -c \'value="      2 1  34 3    2    ";sed "s/ /+/g" <<<$value\'\n++++++2+1++34+3++++2++++\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
~/bin/b43sh -c \'value="      2 1  34 3    2    ";sed "s/ /+/g" <<<$value\'\n2+1+34+3+2\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
sed "s/ \\+/+/g" <<<"0 $value 0"     | tee /dev/tty     | bc\n0+2+1+34+3+2+0\n42\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

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

\n\n

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

\n\n

以及更强大的(强制执行您的假设\xe2\x84\xa2)和可移植的版本:

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

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

\n\n

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

\n\n

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

\n\n

它以多种方式添加参数

\n\n
$ value="      2 1  34 3    2    "\n$ sum "$value"\n42\n$ sum $value    # beware of glob chars *, ? and [  and of odd IFS=123 settings\n42\n$ sum "      2 1  34 3    2    "\n42\n$ sum "      2"    "1  "    "34 3"    "    2    "\n42\n$ var=23\n$ sum "      2"    "1  "    "34 3"    "    2    "   "$var"\n65\n
Run Code Online (Sandbox Code Playgroud)\n