はじめに
DAYMARK は小さなカフェです。閉店時にチームは売上 CSV をダウンロードして帳簿に使います。計算が失敗しても、直前の完全なファイルを置き換えてはいけません。まず合計を理解し、短い Bash 公開ラッパーを完成します。計算プログラムは提供済みです。
基本的な端末操作ができれば十分です。コマンドとシェル構文は使用前に説明します。ポート 8081 の実際のサイト Daymark から始め、コマンドには /srv/daymark-sales に準備された Workstation を使います。データは架空で、インストールや外部アカウントは不要です。
閉店集計を理解して計算する
このステップでは、カフェの元の注文を実際の候補エクスポートと比較します。Daymark で View source records ↗ を開きます。4 行の元データは、単価 300 セントの Espresso が 6 個、250 セントの Tea が 3 個、450 セントの Cake が 2 個で、合計 11 個、3450 セント です。1 セントは通貨単位の 100 分の 1 です。整数のセントを使えば丸めの曖昧さを避けられます。同じ商品の複数行は合算する必要があります。

Reports と Download latest CSV ↓ を開き、実際の CSV ファイルを保存します。CSV はカンマ区切り値を意味し、最初の行に列名があります。初期ファイルは前回の閉店分で TOTAL,1,100 を含みます。完全な置換ファイルができるまでは利用可能な状態を保つ必要があります。DAYMARK でホームに戻ります。
Workstation に切り替えます。pwd は作業ディレクトリ、ls はファイル一覧、cat はテキストファイルを表示します。相対パスは現在のディレクトリから解決されます。
pwd
/srv/daymark-sales と表示されるはずです。
ls
cat orders.csv
各行はサイトと一致します。python3 は提供された計算プログラムを実行します。絶対パスなので現在のディレクトリに依存しません。> は標準出力をファイルにリダイレクトし、プログラムの開始前にそのファイルを置き換えます。公開済み帳票ではなく、独立した候補を使います。
/usr/bin/python3 /opt/labex/d10/summarize.py orders.csv > candidate.csv
cat candidate.csv
item,quantity,value_cents
Cake,2,900
Espresso,6,1800
Tea,3,750
TOTAL,11,3450
元データは単価を持ち、帳票は合算数量と合計金額を持ちます。終了状態 0 は成功、0 以外は失敗です。$? は直前のコマンドの状態に展開されるので、確認したいコマンドの直後に読みます。echo は文字列を表示します。
/usr/bin/python3 /opt/labex/d10/summarize.py fixtures/broken.csv > rejected.csv
echo $?
Export failed: ... と 1 が表示されます。これは意図的な失敗です。計算に失敗しても cat rejected.csv はヘッダーを表示します。部分的な出力は有効な帳票ではありません。独立した候補だけを書いたため、既存のダウンロードは TOTAL,1,100 のままです。パスで失敗したら pwd と ls で確認し、cd /srv/daymark-sales で戻って候補のコマンドを再実行します。cd はディレクトリを変えます。元の注文と既存の帳票を残してください。
成功後だけ公開する
このステップでは、安全でないラッパーを置き換え、保持と新しいデータによる復旧の両方を実演します。Workstation から始めます。ラッパーは入力 CSV と公開パスの 2 引数を受け取ります。内容を読みます。
cat publish.sh
直接の > は、失敗する計算が終わる前に公開ファイルを切り詰めてしまう可能性があります。代わりに出力先の隣に一時ファイルを作り、正常に書き終えてから名前を変更します。同じファイルシステム内の名前変更は、見えるファイル名をアトミックに置き換えます。読み手は完全な旧内容か完全な新内容を見ます。データベースのトランザクションやディスク喪失への保護を提供するものではありません。
この短いラッパーは Bash で実行します。set -e は通常のコマンドが失敗したとき停止し、-u は未設定変数を拒否します。$1 と $2 は 2 つの引数です。name=value は = の前後に空白を置かずシェル変数に代入します。$(command) はコマンド出力を取り込みます。引用符はパスの空白を保持します。mktemp は接尾辞 XXXXXX から一意なファイルを作ります。trap ... EXIT は成功時も失敗時も残った一時データを消します。chmod 644 は所有者に書き込み、全員に読み取りを許可します。mv -fT は指定された出力ファイル名を置き換えます。-T は出力先がディレクトリなら中に移動せず拒否します。printf は公開の詳細、id -u は実際の数値ユーザー ID を表示し、sha256sum はファイルのバイト列を識別します。
cat > publish.sh は自分のラッパーだけを置き換えます。引用符付き SH が here-document を開始します。最後の SH を単独行にして、そこまで貼り付けてください。引用符によりドル記号は文字どおり保持されます。
cat > publish.sh <<'SH'
#!/bin/bash
set -eu
input=$1
output=$2
tmp=$(mktemp "${output}.XXXXXX")
trap 'rm -f "$tmp"' EXIT
/usr/bin/python3 /opt/labex/d10/summarize.py "$input" > "$tmp"
chmod 644 "$tmp"
mv -fT "$tmp" "$output"
printf 'Published %s uid=%s cwd=%s sha256=' "$output" "$(id -u)" "$PWD"
sha256sum "$output"
SH
準備済みの daymark-report アカウントが reports/ を所有します。このアカウントは帳票を書き、サイトは読み取りだけを行います。sudo -u はそのアカウントで実行します。/bin/bash は絶対パスです。明示的に Bash に渡すため、ラッパー自体の実行権限は不要です。
sudo -u daymark-report /bin/bash /srv/daymark-sales/publish.sh orders.csv /srv/daymark-sales/reports/sales.csv
cat reports/sales.csv
完全な TOTAL,11,3450 の帳票が得られます。cp はファイルをコピーします。この演習の orders.csv だけを上書きし、元の fixture は保管します。次に意図的に不正な入力を渡します。
cp fixtures/broken.csv orders.csv
sudo -u daymark-report /bin/bash /srv/daymark-sales/publish.sh orders.csv /srv/daymark-sales/reports/sales.csv
echo $?
意図的な失敗と 1 が表示されます。Daymark に切り替えて Reports を再読み込みし、もう一度ダウンロードして TOTAL,11,3450 が残ることを確認します。失敗した試行はヘッダーだけを公開しても、前のファイルを消してもいけません。
Workstation に戻ります。Tea が 2 個増えた新しい有効な閉店データで復旧します。古い帳票をいつまでも残すのではなく、13 個、3950 セント を出力する必要があります。
cp fixtures/next.csv orders.csv
sudo -u daymark-report /bin/bash /srv/daymark-sales/publish.sh orders.csv /srv/daymark-sales/reports/sales.csv
cat reports/sales.csv
Tea,5,1250 と TOTAL,13,3950 が表示されます。Daymark で Records と Reports を再読み込みし、再度ダウンロードします。元データと帳票が一致しました。権限エラーなら正確な出力パスを確認し、sudo -u daymark-report コマンドを再試行します。全員への書き込み権限は付けないでください。ラッパーに誤字があれば完全な here-document で置き換えて再試行します。一時ファイルは出力の隣に作られるため、最終的な名前変更は同じファイルシステム内にとどまります。
まとめ
完全な CSV の公開、意味のある失敗状態、復旧を練習しました。予定帳票は、実際の実行ユーザー、起動記録、業務合計が一致して初めて役立ちます。この単一 instance の演習は架空データと短い練習用スケジュールを使い、完全な本番帳票基盤ではありません。


