修復が必要なダウンロードを判断する

ShellBeginner
オンラインで実践に進む

はじめに

Field Notes は地域の週末イベントを準備しています。資料パックには PDF ガイド、配布資料、空でもよい任意のクレジットファイルが含まれます。ファイル名ともっともらしいサイズだけでは、ダウンロードの完全性は分かりません。取得する前に、どのファイルに対応が必要かを運営チームに伝える一覧を作成します。

Shell for Beginners で学ぶ基本的な Bash の引数、条件分岐、ループを前提とします。この実習では SHA256、行全体の読み取り、使用する分類方法を説明します。ツールとオリジナルの合成資料は準備済みです。Workspace と Download Desk は同じコンテナを共有し、この実習は独立した状態で始まります。所要時間は、読解と通常の入力ミスへの対応を含む作者の見積もりです。

根拠のある一覧を作成する

このステップでは、信頼できる確認リストから実際のローカルパックの一覧を作ります。パックのファイルは修復も書き換えもしません。

まず使えるファイルを見る

UTF-8 テキストを扱えるよう準備された Workspace から始めます。作業ディレクトリは /home/labex/project/download-desk です。pwd は現在のディレクトリを表示します。相対パスが見つからない場合は ls で確認し、次の完全な cd コマンドでここに戻ります。

cd /home/labex/project/download-desk
ls pack

Download Desk に切り替えます。Welcome guide.pdf の横にある Open file を選びます。新しいブラウザーページに、イベントの予定と東入口の集合場所が表示されるはずです。必要に応じて PDF リーダーのズームを使います。PDF は固定ページであり、モバイル向けに文章が折り返されるレイアウトではありません。Download Desk のページに戻り、そのファイルの横の Download を選んで同じローカルのバイト列を保存します。その後、Workspace に戻ります。

実際のローカル資料へのリンクを表示する Download Desk

Welcome guide.pdf の横の Open file または Download を使います。

このリーダーは、pack に実在するファイルだけを配信します。欠落ファイルを取得元から補うことはできません。テキスト資料はテキストとして、任意の空ファイルは空白ページとして表示される場合があります。この観察だけでは内容の正しさは分かりません。

独立した基準を使う

運営チームは、書き込み可能なパックとは別に /opt/labex/download-pack-state/expected.sha256 を用意しました。SHA256 ハッシュはファイルのバイト列を表す 64 文字の指紋です。独立した信頼できる値との比較により、サイズが変わらない破損も含め、バイト列の違いを検出できます。発行者の身元を証明するものではありません。攻撃者がファイルとチェックサムの両方を提供した場合、一致していてもその発行者を信頼できる根拠にはなりません。確認対象のファイルから基準を作り直さないでください。

cat はファイルを変更せずに表示します。

cat /opt/labex/download-pack-state/expected.sha256

各行は小文字の 64 桁の 16 進ハッシュ、2 個のスペース、完全なファイル名で構成されます。このコースでは、この形式で提供された、読み取り可能で空ではなく、末尾に改行があるマニフェストを使います。名前は階層を持たず、印字可能な ASCII 文字を使い、スペースを含められます。スラッシュ、バックスラッシュ、CR、LF、タブ、および . と .. という名前は対象外です。パックとその親ディレクトリの連なりは実ディレクトリです。記載されたメンバーは通常のローカルファイルか、未存在のいずれかです。リンクされたファイル、ファイルの位置にあるサブディレクトリ、任意のエスケープ付きチェックサム形式には対応しません。この診断実習では、パックの元のファイルと notes.txt をすべて変更せずに残します。

sha256sum はハッシュとファイル名を出力します。二重引用符はスペースを含む名前を 1 つの引数として保ちます。-- はコマンドオプションの終わりを示します。ディレクトリの接頭辞も、- で始まるベース名がオプションとして解釈されることを防ぎます。

sha256sum -- "pack/Workshop card.txt"

このハッシュを基準の行と比較します。両者は異なります。破損ファイルは正しい配布資料と同じバイト数なので、サイズでは問題を発見できませんでした。

sha256sum --check はマニフェストを読み、現在のディレクトリを基準にそのパスを確認します。丸括弧はサブシェルでコマンドを実行します。その中の cd は、外側のターミナルのディレクトリを変更しません。

(cd pack && sha256sum --check /opt/labex/download-pack-state/expected.sha256)

ここで && は、pack への移動に成功した場合だけ確認を実行します。Welcome guide.pdf: OK、Workshop card.txt: FAILED、Map notes.txt: FAILED open or read、Optional credits.txt: OK と警告が表示されるはずです。ファイルの欠落と誤ったバイト列があるため、このコマンドは意図的に失敗します。$? は直前のコマンドのネイティブな終了ステータスです。次のコマンドに置き換えられる前に、すぐ読み取ります。printf は値を整形し、\n は行を終了します。

printf 'Check exit: %s\n' "$?"

期待する表示は Check exit: 1 です。0 バイトのクレジットファイルは、ハッシュが一致するので有効です。「空ではない」はマニフェストの要件であり、すべての資料の要件ではありません。1 行の OK だけでパック全体が正しいとはいえません。

行を読むときに名前を保つ

運営チームには、マニフェストの順に、各行にステータスと完全なファイル名を記す簡潔な一覧が必要です。マニフェストを $1、パックディレクトリを $2 として受け取る inventory.sh を作ります。これは引数とインストール済みコマンドを使う単独のプログラムで、隣接する補助ファイルは不要です。

書く前に、使うシェル構文を確認します。

  • while IFS= read -r line は繰り返しごとに行全体を読みます。空の IFS はスペースの除去を防ぎ、-r はバックスラッシュの解釈を防ぎます。done < "$manifest" はターミナル入力を求める代わりに、マニフェストをループにリダイレクトします。
  • ${line:0:64} は文字位置 0〜63 のハッシュを取り出します。${line:66} は区切りの 2 個のスペースの後をすべて取り出します。ファイル名を単語に分割しません。
  • [ -f "$target" ] は、対応対象が通常ファイルかどうかを確認します。欠落メンバーはチェックサムのコマンドより先に分類します。
  • actual=$(sha256sum -- "$target") はコマンドの出力を取り込みます。${actual%% *} は、スペースとそれに続く任意のテキストに一致する最長の末尾部分を削除し、ハッシュを残します。上記の制限された名前の形式で安全に使えます。
  • printf '%s\t%s\n' は、2 つの値をタブで区切り、改行して出力します。引用符により完全な名前をそれぞれ 1 引数として保ちます。

次のヒアドキュメントは、テキストをそのままスクリプトに保存します。cat > inventory.sh はこのスクリプトファイルを置き換えます。<<'BASH' は BASH だけの行で終わります。区切りの引用符は、保存中に現在のシェルがスクリプト変数を展開することを防ぎます。先頭行は Bash を示します。ここでは明示的に bash で実行します。

cat > inventory.sh <<'BASH'
#!/usr/bin/env bash
manifest=$1
pack=$2
while IFS= read -r line; do
    expected=${line:0:64}
    name=${line:66}
    target=$pack/$name
    status=MISSING
    if [ -f "$target" ]; then
        actual=$(sha256sum -- "$target")
        actual=${actual%% *}
        status=MISMATCH
        if [ "$actual" = "$expected" ]; then
            status=MATCH
        fi
    fi
    printf '%s\t%s\n' "$status" "$name"
done < "$manifest"
BASH

提供された基準とローカルパックを引数に実行します。> は標準出力を inventory.tsv にリダイレクトし、実行のたびにこのレポートを置き換えます。TSV はタブ区切りの値です。このレポートは運営チーム向けの診断結果であり、資料を修復しません。

bash inventory.sh /opt/labex/download-pack-state/expected.sha256 pack > inventory.tsv
cat inventory.tsv

各ステータスとファイル名の間にタブがある、次の行を期待します。

MATCH Welcome guide.pdf
MISMATCH Workshop card.txt
MISSING Map notes.txt
MATCH Optional credits.txt

レポートに MISSING があっても、スクリプトは終了ステータス 0 で終われます。目的は分類であり、パックの完全性を宣言することではないからです。レポートの正しさは、実際のファイルおよび信頼できる基準との関係で決まります。チェックは、別の名前と空ファイルを含む別パックでもスクリプトを試します。このループの書き方自体は必須ではありません。

スクリプトの行を打ち間違えた場合は、ヒアドキュメント全体を再実行して inventory.sh だけを置き換え、レポートを再生成します。誤って pack 内で作業していた場合は、上の絶対パスの cd で戻ります。pack、信頼できる基準、notes.txt は変更しません。一覧作成はパックを読むだけなので、再実行しても同じレポートになるはずです。次の実習では、この分類からダウンロードすべきものを判断します。

まとめ

実際の資料を開き、独立した SHA256 の基準とパックを比較し、行全体を保つ Bash の一覧作成プログラムを作りました。元のパックを変更せずに、欠落、誤ったバイト列、正しい空ファイルを区別できました。この一覧が、後の各修復の根拠になります。