我有一个变量集,其中的数字用空格分隔,其中第一个数字也可以由空格引导,例如:
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