Bash 入門

Bash echo と printf コマンド

Bash には、標準出力(通常はターミナル画面)に内容を表示するための主要なコマンドが2つ用意されています。それが echoprintf です。どちらもユーザーに情報を提示するというタスクを完了できますが、機能面(特にフォーマットやエスケープ文字の解析)においては顕著な違いがあります。これらの差異を理解することは、クリーンで美しく整形されたスクリプト出力を記述するための鍵となります。

1. echo コマンド

echo コマンドは、1行のテキストを表示するための最も直接的でシンプルな方法です。シンプルなメッセージや変数の値の出力、基本的なスクリプトのフィードバックを提供するには、通常これで十分です。デフォルトでは、echo は出力の末尾に自動的に改行(newline)を追加します。

1.1 基本的な使い方

シンプルな文字列を表示するには、引用符(シングルクォートまたはダブルクォート)で囲むだけです。文字列にスペースや特殊文字が含まれていない場合は引用符を省略することもできますが、コードの可読性を高め、予期しないワードスプリッティング(word splitting)やパス名展開(globbing)を防ぐために、引用符を使用する習慣をつけるのがベストプラクティスです。

echo "Hello, Bash!"          # "Hello, Bash!" を表示し、末尾で自動改行
echo Hello, Bash!            # 同様に "Hello, Bash!" を表示(引用符なしでも動作)
echo '第5章へようこそ'          # '第5章へようこそ' を表示

1.2 echo の一般的なオプション

echo は、デフォルトの挙動を変更するためのいくつかのオプションをサポートしています。

-n: 末尾のデフォルト改行をキャンセルします。後続の出力が同じ行に続いて表示されるようにしたい場合に非常に便利です。

echo -n "プロセスを開始しています..." # "プロセスを開始しています..." を表示するが改行しない
sleep 2                       # 2秒間一時停止(デモ用)
echo "完了。"                 # "完了。" は "プロセスを開始しています..." の直後、同じ行に表示される
# 最終出力: プロセスを開始しています...完了。

-e: バックスラッシュ(\)によるエスケープシーケンスの解析を有効にします。-e を付けない場合、echo はバックスラッシュとそれに続く文字を通常のテキストとしてそのまま出力します。よく使われるエスケープシーケンスは以下の通りです。

  • \n: 改行 (Newline)
  • \t: 水平タブ (Horizontal tab)
  • \r: 復帰 (Carriage return、カーソルを行頭に戻す)
  • \: バックスラッシュ自体を表示
  • \c: 現在の echo コマンドの出力をそこで停止させる(-n と似ているが、文字列の途中で使用可能)
echo "1行目\n2行目"             # -e なし:そのまま "1行目\n2行目" と出力
echo -e "1行目\n2行目"          # -e あり:2行に分かれて出力
                               # 1行目
                               # 2行目
echo -e "名前:\t田中太郎"        # "名前:   田中太郎" を表示(タブでインデント)
echo -e "進捗: 50%\r進捗: 100%"  # "進捗: 100%" と表示(\r でカーソルが戻り、上書きされる)
echo -e "出力はここで止まります。\cこの文章は表示されません。" # "出力はここで止まります。" のみ表示

1.3 echo を使った変数の表示

echo の最も一般的な用途の1つは、スクリプト内で定義された変数の値を表示することです。

USER_NAME="Alice"
SERVER_IP="192.168.1.100"

echo "こんにちは、 $USER_NAME!"
echo "サーバーに接続中: $SERVER_IP。"

1.4 実践ケース:シンプルなログ出力

特定の操作の開始時刻と終了時刻を記録するシンプルなスクリプトを考えてみましょう。

#!/bin/bash

OPERATION="データバックアップ"
START_TIME=$(date +"%Y-%m-%d %H:%M:%S")

echo "--- $OPERATION 開始: $START_TIME ---"

# 時間のかかる作業をシミュレート
sleep 3

END_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo "--- $OPERATION 完了: $END_TIME ---"

このスクリプトでは、echo を使用して、タイムスタンプ付きの明確な操作ステータスを提供しています。

2. printf コマンド

printf コマンドは、echo よりも強力で正確なフォーマット(書式設定)能力を提供します。その設計は C 言語の printf 関数から直接着想を得ています。フォーマット文字列(format string)を使用して後続の引数をどのように表示するかを定義し、アライメント(整列)、パディング、数値フォーマットなどを精密に制御できます。

最も核心的な違いは、printf は出力末尾に自動的に改行を追加しないという点です。改行が必要な場合は、フォーマット文字列内に明示的に \n を含める必要があります。

2.1 基本的な使い方とフォーマット文字列

printf の一般的な構文は printf FORMAT [ARGUMENT...] です。FORMAT 文字列には、通常のテキストと、後続の ARGUMENT パラメータのプレースホルダーとなるフォーマット指定子(format specifiers)が含まれます。

printf "Hello, %s!\n" "Bash" # "Hello, Bash!" を表示して改行
printf "数値は %d です。\n" 123  # "数値は 123 です。" を表示
printf "デフォルトでは改行しません。" # 改行されずに表示
printf " 改行しました。\n"      # 前の出力に続いて表示され、最後に改行

2.2 よく使われるフォーマット指定子

  • %s: 文字列 (String)
  • %d または %i: 符号付き10進整数 (Signed decimal integer)
  • %f: 浮動小数点数、10進表記 (Floating-point number)
  • %x または %X: 16進整数(それぞれ小文字、大文字に対応)
  • %o: 8進整数 (Octal integer)
  • %c: 単一文字 (Single character)
  • %%: リテラルのパーセント記号 % 自体を表示

2.3 フラグと幅修飾子

printf は、極めて細かな制御を実現するための複数のフラグ(flags)と幅修飾子を提供しています。

  • 幅 (Width): %-10s(左揃えで10文字分の幅を確保)、%10s(右揃えで10文字分の幅を確保)。
  • 精度 (Precision): %.2f(小数点以下2桁の浮動小数点数)、%.5s(文字列を最大5文字で切り捨て)。
  • パディング (Padding): デフォルトではスペースで埋められます。数値を 0 で埋めたい場合は、0 フラグを使用します:%05d(先行ゼロで5桁に埋める)。

一般的なフラグ (Flags):

  • -: 設定されたフィールド幅内で左揃えにする。
  • +: 数値の出力に対して強制的に正負の符号を表示する(例: +5, -3)。
  • (スペース): 正数の前にスペースを1つ、負数の前にマイナス記号を表示する。
  • 0: 数値タイプの出力に対して先行ゼロで埋める。

2.4 printf フォーマットの詳細例

文字列のフォーマット (%s)

NAME="Jane Doe"
ROLE="Developer"
TEAM="Backend"

# デフォルトの文字列出力
printf "名前: %s, 役割: %s\n" "$NAME" "$ROLE"

# 左揃え、幅20文字
printf "名前: %-20s役割: %s\n" "$NAME" "$ROLE"

# 右揃え、幅20文字
printf "名前: %20s役割: %s\n" "$NAME" "$ROLE"

# 文字列を最大5文字で切り捨て
printf "イニシャル/短縮名: %.5s\n" "$NAME"

整数のフォーマット (%d)

USER_ID=1001
LOGIN_COUNT=42

# デフォルトの整数出力
printf "ユーザーID: %d, ログイン回数: %d\n" "$USER_ID" "$LOGIN_COUNT"

# 先行ゼロで埋めて計5桁にする
printf "ユーザーID: %05d\n" "$USER_ID"

# 右揃え、幅8文字分を確保
printf "ログイン回数: %8d\n" "$LOGIN_COUNT"

# 正数に対して強制的にプラス記号を表示
printf "残高: %+d\n" 150
printf "残高: %+d\n" -75

浮動小数点数のフォーマット (%f)

TEMPERATURE=23.456
PRICE=9.99

# デフォルトの浮動小数点出力(通常、小数点以下が長くなる)
printf "温度: %f\n" "$TEMPERATURE"

# 小数点以下2桁まで表示
printf "温度: %.2f 度\n" "$TEMPERATURE"

# 右揃え、幅10文字分、小数点以下2桁
printf "価格: %10.2f ユーロ\n" "$PRICE"

16進数と8進数のフォーマット (%x, %o)

DEC_VALUE=255

printf "10進数: %d\n" "$DEC_VALUE"
printf "16進数 (小文字): %x\n" "$DEC_VALUE"
printf "16進数 (大文字): %X\n" "$DEC_VALUE"
printf "8進数: %o\n" "$DEC_VALUE"

printf でのエスケープ

echo -e と同様に、printf はデフォルトでフォーマット文字列内のバックスラッシュエスケープシーケンスを解釈します。

printf "1行目\n2行目\n"               # 改行
printf "項目\t数量\t価格\n"             # タブ
printf "処理中...\r完了しました。\n"    # 復帰("処理中..." を上書き)

2.5 静的テキストと変数の結合表示

printf は、構造化された方法で静的テキストと動的データを組み合わせることに長けており、特に整列されたテーブル(表)を出力するのに適しています。

#!/bin/bash

# データの定義
ITEM_NAME="ノートPC用電源"
ITEM_ID="LC-X123"
QUANTITY=5
UNIT_PRICE=25.50

# bc コマンドを使用して浮動小数点の乗算計算
TOTAL_COST=$(echo "$QUANTITY * $UNIT_PRICE" | bc)

# ヘッダーの出力
printf "%-20s %-15s %-10s %-10s\n" "商品名" "商品ID" "数量" "単価"
printf "%-20s %-15s %-10s %-10s\n" "---------" "-------" "---" "-----"

# 商品詳細の出力
printf "%-20s %-15s %-10d %-10.2f\n" "$ITEM_NAME" "$ITEM_ID" "$QUANTITY" "$UNIT_PRICE"

# 合計の出力("" 空文字列を使用して位置合わせを調整)
printf "\n合計費用: %-35s %.2f ユーロ\n" "" "$TOTAL_COST"

この例では、整然としたテーブル形式の出力を作成する方法を示しています。これを echo で実現しようとすると、処理が非常に煩雑になり、位置がずれやすくなります。

3. echo と printf、どちらを使うべきか?

echo を推奨するシナリオ:

  • 複雑なフォーマットを必要としない、シンプルなメッセージ出力。
  • デバッグ(Debugging)のための迅速な変数値の確認。
  • デフォルトで末尾の改行が必要で、たまに改行をキャンセル(-n)する程度で済む場合。
  • エスケープ文字を解析する必要がほとんどない場合。

printf を推奨するシナリオ:

  • 整列、パディング文字、数値の精度の制御など、出力に厳密なフォーマット要件がある場合。
  • テーブルデータや、規定の形式に従ったレポート・帳票を作成する場合。
  • 改行するかどうかを正確に制御する必要がある場合(明示的に \n を使用)。
  • 異なる種類のデータ(文字列、整数、浮動小数点数)を混在させ、統一された形式で表示する必要がある場合。
  • ポータビリティ(Portable)が高く、堅牢なスクリプトを記述する場合。printf の挙動は異なる Unix 系システム間でも echo より差異が少なく、標準化されています。

3.1 ケース比較:ディスク使用量レポート

システム管理者にディスクスペースの使用状況を報告するスクリプトを想定します。

echo を使用した場合(構造がルーズで、整列が困難):

#!/bin/bash
DEVICE="/dev/sda1"
MOUNT_POINT="/mnt/data"
TOTAL_SIZE="100G"
USED_SPACE="75G"
FREE_SPACE="25G"

echo "ディスク使用レポート: $DEVICE ($MOUNT_POINT):"
echo "合計: $TOTAL_SIZE"
echo "使用済み: $USED_SPACE"
echo "空き: $FREE_SPACE"
echo
echo "備考: 警告閾値を超えています。直ちに対処してください。"

この出力は人間が読むことはできますが、整列されていません。TOTAL_SIZE などの変数の長さが変わると、見た目がガタガタになります。

printf を使用した場合(構造化され、可読性が高い):

#!/bin/bash
DEVICE="/dev/sda1"
MOUNT_POINT="/mnt/data"
TOTAL_SIZE="100G"
USED_SPACE="75G"
FREE_SPACE="25G"
USAGE_PERCENT=75

printf "--- ディスク使用レポート: %s ---\n" "$MOUNT_POINT"
printf "%-20s %s\n" "物理デバイス:" "$DEVICE"
printf "%-20s %s\n" "マウントポイント:" "$MOUNT_POINT"
printf "%-20s %s (使用率: %d%%)\n" "合計/使用/空き:" "${TOTAL_SIZE}/${USED_SPACE}/${FREE_SPACE}" "$USAGE_PERCENT"
printf "\n"
printf "システムステータス: %s\n" "警告閾値を超えています。直ちに対処してください。"

printf バージョンの出力は、整然としており、常に一貫して整列されたレポートを生成します。プロフェッショナルな環境では、スクリプトが生成するレポートやログファイルは、プログラムによる解析や人間によるスキャンが容易である必要があるため、このような一貫性を保つことが極めて重要です。