Bash 入門

Bash 数値处理

Bashスクリプトプログラミングにおいて、バリアブル(変数)はデフォルトで主にストリング(文字列)として扱われますが、数値に対して算術演算を実行する能力は、堅牢で動的なオートメーション(自動化)スクリプトを作成する上で不可欠です。システムパフォーマンスの指標を処理する場合でも、サーバーのリソース使用率を計算する場合でも、あるいは単純なループカウンタを管理する場合でも、整数(Integer)の扱いと数学的な計算方法を理解することはコアスキルとなります。本章では、Bashスクリプト内で効率的に整数演算を実行するためのツールとテクニックを提供し、今後のレッスンで扱うより複雑なロジック操作やデータクリーニングのための強固な基礎を築きます。

1. Bashにおける「数値」の本質を理解する

デフォルトの状態では、Bashのバリアブルは、その値が完全に数字に見えたとしても、すべての値をストリングとして保存します。

例えば、COUNT=5INCREMENT=3 を実行した場合、Bashは当初 COUNTINCREMENT を単なるテキストの "5" と "3" として扱います。もし単純に echo "$COUNT + $INCREMENT" と入力してこれらを足そうとすると、Bashはターミナル5 + 3 と出力します。これは数学的な加算ではなく、ストリングとしての連結(Concatenation)に近い処理を行っているためです。

本物の数学的演算を実行するには、Bashに対して明示的にこれらのストリング値を「整数」として解釈するよう指示する必要があります。Bashはこのためにいくつかのメカニズムを提供しています。

2. 算術式展開 $((expression)):現代的な推奨手法

Bashで整数演算を実行する際に最も一般的で柔軟、かつ現代的な方法は、算術式展開(Arithmetic Expansion)を使用することです。その構文構造は $((expression)) です。この構造は、括弧内の expression を数式として計算し、最終的な結果をコマンドラインに置き換えます。

利便性のポイント: ((...)) 構造の内部では、バリアブルを参照する際に先頭の $ 記号は不要です。Bashは自動的にそれらを整数バリアブルとして認識します。

2.1 基本演算子:加、減、乗、除、剰余

算術式展開は、すべての標準的な数学的演算子(Operator)をサポートしています。

  • + : 加算
  • - : 減算
  • * : 乗算
  • / : 除算 (注意: Bashは整数除算のみをサポートします。小数点以下は切り捨てられます)
  • % : 剰余 (除算後の余り)
#!/bin/bash

num1=10
num2=3
result=""

echo "--- 基本算術演算 ---"

# 加算
result=$((num1 + num2))
echo "加算: $num1 + $num2 = $result" # 出力: 13

# 減算
result=$((num1 - num2))
echo "減算: $num1 - $num2 = $result" # 出力: 7

# 乗算
result=$((num1 * num2))
echo "乗算: $num1 * $num2 = $result" # 出力: 30

# 除算 (整数除算)
result=$((num1 / num2))
echo "除算 (整数): $num1 / $num2 = $result" # 出力: 3 (実際の結果 3.333... の小数点以下が切り捨て)

# 剰余 (余り)
result=$((num1 % num2))
echo "剰余: $num1 % $num2 = $result" # 出力: 1 (10 を 3 で割ると商が 3、余りが 1)

# 構造内部では '$' 記号は不要
count=5
increment=2
new_count=$((count + increment))
echo "変数直接使用: count ($count) + increment ($increment) = $new_count" # 出力: 7

# 複合代入 (計算結果を元の変数に反映)
initial_value=15
initial_value=$((initial_value + 5))
echo "5 を追加した後: $initial_value" # 出力: 20

2.2 指数演算 (累乗)

Bashの算術式展開は、 ** 演算子を使用した指数演算(Power)もサポートしています。

#!/bin/bash

base=2
power=3
result=$((base ** power))
echo "$base の $power 乗は: $result" # 出力: 8 (つまり 2*2*2)

2.3 演算優先順位 (Order of Operations)

Bashは、標準的な数学的演算優先順位(乗除が先、加減が後)に従います。小括弧 () を使用して、デフォルトの優先順位を変更することも可能です。

#!/bin/bash

a=2
b=3
c=4

# 括弧なし: 乗算が先、加算が後
result=$((a + b * c))
echo "$a + $b * $c = $result" # 出力: 14 (3*4=12, その後 2+12=14)

# 括弧あり: 加算を強制的に先に行う
result=$(((a + b) * c))
echo "($a + $b) * $c = $result" # 出力: 20 (2+3=5, その後 5*4=20)

2.4 「ゼロ除算 (Division by zero)」エラーへの対応

数学において、ゼロで割る操作は未定義です。Bashの算術式展開でゼロ除算を試みると、通常は実行時に深刻なエラーが発生し、スクリプトが停止することがあります。

dividend=10
divisor=0

# この操作は深刻なエラーを引き起こします
result=$((dividend / divisor)) # ターミナルでのエラー: bash: 10 / 0: division by 0 (error token is "0")

スクリプトを作成する際、特に除数(divisor)がユーザー入力や動的に取得したシステムデータに依存する場合は、細心の注意が必要です。除算を実行する前にチェックメカニズム(これについてはモジュール3の条件分岐で学習します)を追加し、このようなクラッシュを防ぐ必要があります。

3. let コマンド:代入とインクリメント/デクリメント

let コマンドは、整数演算バリアブルへの代入を実行するためのもう一つの方法を提供します。算術式展開と同様に、let もそれに続く引数を算術式として評価します。

3.1 基本的な使い方

let を使用する場合、式内部のバリアブルにも $ を付ける必要はありません。ただし、式の中にスペースや特殊文字が含まれる場合は、式全体をダブルクォートで囲む必要があります。

x=10
y=5

# 基本的な加算
let sum=x+y
echo "和: $sum" # 出力: 15

# スペースを含む乗算(クォートが必要)
let "product = x * y" 
echo "積: $product" # 出力: 50

3.2 変数のインクリメントとデクリメント (自増/自減)

let は、C言語スタイルのショートカット演算子である ++-- を完璧にサポートしており、単純な加算や減算の操作に非常に便利です。もちろん、$ を付けない ((...)) 構造でも同様のことが可能です。

counter=0

# let を使用したインクリメント
let counter++
echo "let counter++ の後: $counter" # 出力: 1

# let を使用した複合代入
let counter+=5 # counter = counter + 5 と同等
echo "let counter+=5 の後: $counter" # 出力: 6

# ((...)) 構造による直接操作 (推奨される、より簡潔な方法)
num=10
((num++)) # num を 1 増やす
echo "((num++)) の後: $num" # 出力: 11

((num-=2)) # num を 2 減らす
echo "((num-=2)) の後: $num" # 出力: 9

ヒント: 先頭に $ が付かない ((...)) は、独立した「算術評価コマンド」です。テキストとしての結果を返さず、バックグラウンドで直接バリアブルの値を変更します。これは if ステートメント内の条件判断としてよく使われます。

4. expr コマンド (レガシーな方法)

expr コマンドは、Bashの組み込み機能ではなく外部ツールであり、式を計算するために使用されます。これは、Bashや他のシェルで数学演算を行うための古い手法です。現在でも利用可能ですが、構文が冗長でパフォーマンスが低いため(外部プログラムを呼び出す必要があるため)、現代のスクリプトではほぼ $((...)) に置き換えられています。

expr の特筆すべき注意点は、各演算子と数値の間に必ずスペースを入れなければならないこと、そして *(乗算記号であり、ワイルドカードでもある)などの特殊文字をエスケープしなければならないことです。

val1=20
val2=4

# 加算 (記号の両側にスペースが必要)
sum=$(expr $val1 + $val2) 

# 乗算 (アスタリスクをバックスラッシュでエスケープ)
product=$(expr $val1 \* $val2) 
echo "expr 乗算: $product" # 出力: 80

expr を知っておくことは、古いレガシースクリプトを読み解く際に役立ちますが、自身の新しいプロジェクトでは $((...)) を優先して使用してください。

5. 実戦演習:システム管理における算術演算の応用

整数演算をシステム管理タスクに取り入れ、これまで学んだバリアブルの知識と組み合わせてみましょう。

5.1 システム稼働時間 (Uptime) の計算とフォーマット

システムの生の稼働時間は、通常「秒」単位で提供されます。整数演算(特に除算と剰余)を使用して、これを人間が読みやすい「時間、分、秒」の形式に変換できます。

#!/bin/bash

# システムから取得した総稼働時間が 3665 秒であると仮定します
total_uptime_seconds=3665 

echo "総稼働秒数: $total_uptime_seconds"

# 1. 時間(Hours)を計算 (1時間は 3600 秒)
hours=$((total_uptime_seconds / 3600)) 

# 2. 時間を引いた残りの秒数を計算
remainder_seconds=$((total_uptime_seconds % 3600))

# 3. 残りの秒数から分(Minutes)を計算 (1分は 60 秒)
minutes=$((remainder_seconds / 60))

# 4. 最後に残ったのが秒(Seconds)
final_seconds=$((remainder_seconds % 60))

echo "フォーマット後のシステム稼働時間: $hours 時間, $minutes 分, $final_seconds 秒"
# 出力: フォーマット後のシステム稼働時間: 1 時間, 1 分, 5 秒

5.2 基本的なサーバーリソース割り当てのシミュレーション

限られた「接続スロット」や「リソース」を管理するスクリプトを想定します。算術演算を使用して、リアルタイムで割り当て状況を追跡できます。

#!/bin/bash

total_slots=100
allocated_slots=35
user_request=15

echo "総スロット数: $total_slots"
echo "割り当て済みスロット: $allocated_slots"

# 残りスロットの計算
remaining_slots=$((total_slots - allocated_slots))
echo "空きスロット: $remaining_slots" # 出力: 65

# ユーザーによる新しいスロット要求のシミュレーション
echo "ユーザーが $user_request 個のスロット割り当てを要求しています..."
new_allocated=$((allocated_slots + user_request))
new_remaining=$((total_slots - new_allocated))

echo "割り当て後のステータス: 使用済み = $new_allocated, 残り = $new_remaining"
# 出力: 割り当て後のステータス: 使用済み = 50, 残り = 50