HOWTO · Bash

Bash を使用してテキスト ファイル内の数値のリストを合計する

この記事は、Bash スクリプトを使用してテキスト ファイル内の数値のリストを合計する方法に関する短いチュートリアルです。

このページの内容

テキストファイルには、1行に1つの数値、列に分かれた値、または stdin から渡されるデータを格納できます。1行に1つの数値なら awk が簡潔です。paste と bc を組み合わせると、連結した式を評価できます。Bash のループなら、各行に対する処理を追加できます。適切な方法は、入力形式と、小数計算や独自の処理が必要かどうかによって異なります。

以下の例では、numbers.txt ファイルに整数 1 から 10 までを1行ずつ格納します。

1行に1つの数値を awk で合計する

awk は入力の各行を処理し、最初のフィールドを sum に加算します。ファイルを読み終えると END ブロックが実行され、累計値を出力します。行に複数のフィールドがある場合も使えます。$1 は空白区切りの最初のフィールドを選びます。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

numbers_file=$(mktemp)
trap 'rm -f "$numbers_file"' EXIT
printf '%s\n' 1 2 3 4 5 6 7 8 9 10 > "$numbers_file"
awk '{ sum += $1 } END { print sum }' "$numbers_file"

空白区切りデータの別のフィールドを合計するには、2番目のフィールドに $2 を使うか、対象のフィールド番号を指定します。既定では awk は連続する空白文字をフィールド区切りとして扱います。

1行に1つの数値を paste と bc で合計する

paste -s -d '+' は入力行を1つの式に連結し、レコードの間にプラス記号を挿入します。bc がその式を評価します。この方法では1行に1つの数値を想定しています。paste は、空白やタブで区切られた複数の値を、行内で別々の加算項に分割しません。

bc がインストールされている場合は、次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

numbers_file=$(mktemp)
trap 'rm -f "$numbers_file"' EXIT
printf '%s\n' 1 2 3 4 5 6 7 8 9 10 > "$numbers_file"
if command -v bc >/dev/null 2>&1; then
    paste -s -d '+' "$numbers_file" | bc
else
    printf '%s\n' 'Install bc to evaluate the joined expression.'
fi

bc は小数計算に対応していますが、除算の精度は scale の設定に左右されます。小数の加算に除算は必要ありません。この例では bc の有無を確認するため、ユーティリティが利用できない場合もシェルスクリプトは有用なメッセージを表示して終了します。

標準入力から数値を読み取る

コマンドは名前を指定したファイルを読み取ることも、stdin からデータを受け取ることもできます。同じ awk のプログラムをどちらにも使えます。パイプでは、データを渡すコマンドが cat の代わりになります。ファイルを表示するだけのコマンドを追加せずに済みます。

このセクションのコードと表示されるラベルや出力は英語のままです。

numbers_file=$(mktemp)
trap 'rm -f "$numbers_file"' EXIT
printf '%s\n' 1 2 3 4 5 6 7 8 9 10 > "$numbers_file"
awk '{ sum += $1 } END { print sum }' "$numbers_file"
printf '%s\n' '1 2 3 4 5 6 7 8 9 10' | awk '{ for (i = 1; i <= NF; i++) sum += $i } END { print sum }'

最初のコマンドはファイルを直接読み取ります。2番目のコマンドはパイプから空白区切りの数値を受け取り、入力行のすべてのフィールドを加算します。例にある printf の代わりに、レコードを生成する別のコマンドを使うこともできます。

選択した列を合計する

フィールドが1つの空白文字で区切られているデータなら、cut でフィールドを選択できます。フィールド番号は1から始まるため、-f 2 は2番目のフィールドを選びます。選択した値は awk で合計できます。

このセクションのコードと表示されるラベルや出力は英語のままです。

records_file=$(mktemp)
trap 'rm -f "$records_file"' EXIT
printf '%s\n' 'Ada 12' 'Lin 8' 'Sam 5' > "$records_file"
cut -d ' ' -f 2 "$records_file" | awk '{ sum += $1 } END { print sum }'

この区切り文字の指定には注意が必要です。cut -d ' ' は1つの空白文字を区切りとして扱い、連続する空白やタブを正規化しません。空白文字の数が一定でない場合は、各レコードの $2 を加算するなど、awk を直接使います。

独自の処理が必要な場合は Bash のループを使う

合計に加える前に各行を判定したり、別の処理を加えたりする場合は、ループが便利です。この例では、末尾に改行がない最終行も処理します。

このセクションのコードと表示されるラベルや出力は英語のままです。

numbers_file=$(mktemp)
trap 'rm -f "$numbers_file"' EXIT
printf '1\n2\n3\n4\n5\n6\n7\n8\n9\n10' > "$numbers_file"
sum=0
while IFS= read -r number || [[ -n $number ]]; do
    sum=$((sum + number))
done < "$numbers_file"
printf '%s\n' "$sum"

ここでの Bash の算術演算は整数を扱います。小数には bc や awk などのツールを使います。このループは、空行以外の各行に Bash の算術演算で解釈できる整数が含まれることを前提とします。ファイルにヘッダーやその他のテキストが含まれる可能性がある場合は、先に入力を検証するか、不要な行を除外してください。

方法を選び、入力の制限に注意する

1行に1つの数値がある場合や、空白区切りの列からフィールドを選ぶ場合は awk を使います。各行が算術式の1項で、小数計算も必要なら、paste と bc を組み合わせます。合計の計算に各行ごとの独自処理が必要なら、Bash のループを選びます。

これらのコマンドは入力をそれぞれ異なる方法で解釈します。paste は行全体を連結し、cut は指定した区切り文字を使い、awk はレコードをフィールドに分割します。ファイルで実際に使われている区切り文字に合ったコマンドを選んでください。列が可変個数の空白文字で区切られている場合は、一般に awk を使うのが簡単です。

入力に、選んだツールが想定する種類の数値が含まれていることを確認してください。Bash の算術演算は整数を扱います。一方、bc と awk の小数の扱いは、それぞれのツールの算術規則に従います。非常に大きな値は、ツールやシステムの数値範囲、または実用上の処理能力を超える場合もあります。適切な数値処理ツールを選び、実際に処理するデータで検証してください。