web-dev-qa-db-ja.com

bash-変数の数値を合計する

スペースで区切られた数値を含む変数セットがあります。最初の数値はスペースで区切ることもできます。例:

VAR=" 2 1  34 3    2 "

これらの数値をすべて合計する必要があります。最も簡単な方法は、数値間のすべてのスペースを+で置き換え、bcでパイプすることです。

Forループ、paste、およびbcでそれを行うことができますが、おそらく誰かがそれを行う簡単な方法を知っていますか?多分いくつかの計算は、bash組み込み文字列置換を使用してVARのbashで直接行いますか?

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

更新:すべての提案に対するThx、配列を使用した非常に短いメソッドが最終的に見つかりました:

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

パラメータ展開を使用します。

#!/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.
4
choroba

いくつかの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

またはGNU sedおよびcoreutils:

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

Chrisの配列ソリューションと同様:

$ var=" 2 1  34 3    2 "
$ var=$(echo $var); echo "$((${var// /+}))"
42

すべてのスペース文字をトリミングしてスクイーズしてから、すべてのスペース文字を+に置き換えて評価します。

4
Freddy

ソース文字列にスペースと先頭/末尾のスペースが繰り返されています。

単純なスペースの_+_への変換は失敗します:

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

すべての繰り返されたスペースを折りたたむand先頭/末尾のスペースを削除するには、引用符で囲まれていない変数のエコー(またはprintf)が必要です(IFSがデフォルトであると想定)。

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

そして、それはbcにフィードすることができます:

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

必要な場合は、すべて1行で:

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
_

純粋なシェルソリューション(置換_${//}_部分にbash、kshまたはzshが必要)は次のようになります。

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

さらにrobust(仮定を適用する™)およびportableバージョン:

  • iFSの変更が現在のシェルに影響を与えることは避けてください。サブシェル_(…)_を使用します。
  • iFSがスペースの値を分割するようにします(IFS = "")
  • 引用符で囲まれていない場合でも、文字列が_*_ et all(set -f)を展開しないことを確認します。
  • パーツが_+_(IFS = +)で結合されていることを確認してください。

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

機能バージョン
1-シェルがlocalを許可しない場合は、より遅いサブシェルフォームを使用します
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
_
4
Isaac

bashスクリプト:

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

変数の長さがわかっている場合は、次のようにawkを使用できます。

echo $VAR | awk '{print $1 + $2 + $3 + $4 + $5}'

出力:

42

これがお役に立てば幸いです。

1
Shmuel

構成要素の数値が整数である限り、これも機能します(これは、declareまたはtypesetが整数式を初期化する算術式を受け入れるためです)

VAR=" 2 1  34 3    2 "
read -r VAR <<<$VAR #shed leading and trailing spaces, compact internal spaces
VAR=${VAR// /+}   #replace spaces with plusses
declare -i var=$VAR     #typeset var to an integer
echo $var
42
0
iruvar