10. ハンズオン課題

読むだけでは身につかない部分を、手を動かして埋める

📅 作成: 2026-08-24 / 更新: 2026-08-24 / 対象: Windows PowerShell 5.1 + PowerShell 7

この章の位置づけ

01〜09 は読んで理解するための資料でした。この章は書いて確かめるためのものです。勉強会の各回で 35 分のハンズオン時間を想定しています。

課題はすべて「動くところまで」を判定基準にしています。
きれいなコードを書くことが目的ではありません。自分の環境で実際に動かし、期待どおりの出力が出ることを確認してください。動いてから整えれば十分です。
掲載している解答例はすべて実行して確認済みです。詰まったら見て構いませんが、まず 10 分は自力で試してください。

10.1 進め方と準備

課題と章の対応

課題 得られるもの 第1回 / 01・02 文字化けを再現して直す BOM が何をしているかを体感する 第2回 / 03・04 ランチャー付き挨拶スクリプト 配布できる形を作れるようになる 第3回 / 05 プロセスをメモリ順に上位5件 Get-Member で自力で調べられる 第4回 / 06・07 ファイル一覧を CSV 出力 関数化と文字コード指定ができる 第5回 / 08・09 ログ集計スクリプトを仕上げる 実務で使える1本が手元に残る
図 10.1 — 各回の課題は、その回で読んだ章の内容だけで解ける

事前に用意するもの

項目内容いつまでに
作業フォルダC:\work\ps-lesson\第1回
VS CodePowerShell 拡張機能を入れておく(03.4)第1回
PowerShell 7winget install --id Microsoft.PowerShell第2回
実行ポリシーSet-ExecutionPolicy -Scope CurrentUser RemoteSigned第2回
サンプルログ下記のスクリプトで生成第5回

サンプルログを作る

# 課題5 用のログを生成する(そのまま実行してください)
$dir = 'C:\work\ps-lesson\logs'
New-Item -ItemType Directory -Force -Path $dir | Out-Null

$levels = 'INFO', 'INFO', 'INFO', 'WARN', 'ERROR'
$msgs   = 'ユーザーがログインしました user=tanaka',
          '応答が遅延しています ms=1520',
          '接続に失敗しました host=db01',
          'バッチが完了しました count=120',
          'タイムアウト host=api02'

$lines = foreach ($i in 1..200) {
    $d = (Get-Date).AddDays(-(Get-Random -Minimum 0 -Maximum 10))
    '{0} {1:HH:mm:ss} {2,-5} {3}' -f $d.ToString('yyyy-MM-dd'), $d,
        ($levels | Get-Random), ($msgs | Get-Random)
}

$lines | Sort-Object | Set-Content (Join-Path $dir 'app.log') -Encoding UTF8
Write-Host "$($lines.Count) 行のログを作成しました"

進め方のルール

「1行ずつ実行して確かめる」が最も効率的です。
Get-Process | Sort-Object ... | Select-Object ... を一気に書いて動かないと、どこが悪いのか分かりません
Get-Process だけ実行 → 結果を見る → | Sort-Object ... を足す → また結果を見る、と1段ずつ伸ばすと、間違えた瞬間に気づけます。PowerShell の対話環境はこの進め方に向いています。

10.2 課題 1〜5

課題1 — 文字化けを再現して直す 第1回 / 01・02章

目標

同じファイルが、保存形式と実行するバージョンによって文字化けしたりしなかったりすることを、自分の目で確認します。

手順

  1. VS Code で hello.ps1 を作り、Write-Host "こんにちは、PowerShell" と1行書く
  2. 右下のエンコーディング表示から「UTF-8」(BOM なし)で保存する
  3. powershell.exe -File .\hello.ps1 で実行する 5.1
  4. pwsh.exe -File .\hello.ps1 で実行する pwsh 7
  5. 「UTF-8 with BOM」で保存し直し、3〜4 をもう一度実行する

完成の判定

保存形式5.1 での表示7 での表示
UTF-8(BOM なし)文字化け正常
UTF-8(BOM 付き)正常正常

発展課題

# 先頭3バイトを見て BOM の有無を確認する
Get-Content .\hello.ps1 -AsByteStream -TotalCount 3      # pwsh 7
# → 239 187 191 (EF BB BF)なら BOM 付き

課題2 — ランチャー付き挨拶スクリプト 第2回 / 03・04章

目標

ダブルクリックで動く形を作ります。02.4 の内容をそのまま手を動かして確認します。

手順

  1. greet.ps1 を作る。引数 -Name を受け取り、時刻に応じて挨拶を変える
  2. greet.cmd を作る。Shift-JIS + CRLF で保存する
  3. エクスプローラーから greet.cmd をダブルクリックする

完成の判定

発展課題

課題3 — プロセスをメモリ順に上位5件 第3回 / 05章

目標

Get-Member で自分で調べて解きます。「メモリ使用量のプロパティ名」はあえて教えません

手順

  1. Get-Process | Get-Member で、メモリに関係するプロパティを探す
  2. そのプロパティで降順に並べる
  3. 上位5件だけ取り出す
  4. バイト単位では読みにくいので、MB に変換した列を作る

完成の判定

プロセス           メモリMB    Id
--------           --------    --
Memory Compression  1081.50  3316
claude               433.50 49708
Discord              295.60 39604
explorer             289.60 30292
vmmemWSL             263.10 43988

発展課題

課題4 — ファイル一覧を CSV 出力 第4回 / 06・07章

目標

処理を関数に切り出し、結果を文字化けしない CSV に出します。

手順

  1. Get-FileReport という関数を作る(引数: -Path-Filter
  2. 再帰的にファイルを集め、名前・フォルダ・サイズMB・更新日の4列にする
  3. サイズの大きい順に並べて画面表示する
  4. Export-Csv でファイルに出す
  5. 出力した CSV を Excel で開き、日本語が化けないことを確認する

完成の判定

名前                   フォルダ サイズMB 更新日
----                   -------- -------- ------
05-パイプライン.html   docs         0.05 2026-08-23 08:26
02-ファイル形式.html   docs         0.05 2026-08-22 23:19
...

発展課題

課題5 — ログ集計スクリプトを仕上げる 総合

目標

ここまでの総合課題です。実務でそのまま使える1本を完成させます。

要件

要件関連章
引数でログのパス集計日数を受け取る(日数の既定は 7)06.2
ファイルが存在しない場合、分かりやすいメッセージを出して終了コード 1 で終わる08.2
日付 × レベルで件数を集計して表示する05.4
ERROR が1件以上あれば、詳細を赤字で出す08.4
正常終了時は終了コード 0 を返す08.3
-Verbose で処理の進行が見える06.2・08.4

完成の判定

発展課題

10.3 解答例とつまずきポイント

つまずきの分布

課題 最も多い詰まり 1 文字化け VS Code で保存し直したつもりが、エンコーディングが変わっていない 2 ランチャー cmd を UTF-8 で保存してしまい、日本語のメッセージが化ける 3 プロセス 画面に出ている列名だけを探し、WorkingSet64 に辿り着けない 4 CSV 計算プロパティ @{N=...; E={...}} の書き方でつまずく 5 ログ集計 try/catch を書いたのに catch に入らない(-ErrorAction Stop の付け忘れ)
図 10.2 — 各課題で最も報告が多い詰まり。いずれも本編で扱った内容

課題3 の解答例

Get-Process |
    Sort-Object WorkingSet64 -Descending |
    Select-Object -First 5 -Property @{ N='プロセス'; E={ $_.ProcessName } },
        @{ N='メモリMB'; E={ [math]::Round($_.WorkingSet64 / 1MB, 1) } },
        Id |
    Format-Table -AutoSize
つまずき: メモリのプロパティ名が見つからない
画面に出ているのは WS(M) という表示用の名前で、実際のプロパティ名ではありません(05.1)。Get-Process | Get-Member -MemberType Property で一覧すると WorkingSet64 が見つかります。
「画面の列名 ≠ プロパティ名」という 05 章の内容が、そのまま試される課題です。
つまずき: Select-Object -First 5 の位置
Select-Object -First 5Sort-Objectに書くと、「最初の5件を取ってから並べ替える」ことになり答えが変わります
パイプラインは書いた順に流れるので、「全部並べてから上位を取る」順序で書いてください。

課題4 の解答例

function Get-FileReport {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Path,
        [string]$Filter = '*'
    )
    Get-ChildItem -LiteralPath $Path -Recurse -File -Filter $Filter |
        ForEach-Object {
            [PSCustomObject]@{
                名前     = $_.Name
                フォルダ = $_.Directory.Name
                サイズMB = [math]::Round($_.Length / 1MB, 3)
                更新日   = $_.LastWriteTime.ToString('yyyy-MM-dd HH:mm')
            }
        }
}

$report = Get-FileReport -Path 'C:\work\ps-lesson' -Filter '*.html'
$report | Sort-Object サイズMB -Descending | Format-Table -AutoSize
$report | Export-Csv -LiteralPath '.\report.csv' -Encoding UTF8 -NoTypeInformation
つまずき: 計算プロパティと [PSCustomObject] の使い分け
どちらでも書けます。列が3つ以上あるなら [PSCustomObject]@{ } のほうが読みやすいので、上の解答ではそちらを使いました。
Select-Object の計算プロパティ(@{ N=...; E={...} })は、既存の列に1〜2列足すときに向いています。
つまずき: Excel で開くと文字化けする
pwsh 7-Encoding UTF8BOM なしで出力されます(07.3)。Excel は BOM なし UTF-8 の CSV を ANSI と誤認することがあります。
Excel で直接開くなら -Encoding utf8BOM を指定するか、Excel 側で[データ]→[テキストから]でエンコーディングを指定して読み込んでください。

課題5 の解答例

[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$LogPath,
    [int]$Days = 7
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

try {
    if (-not (Test-Path -LiteralPath $LogPath -PathType Leaf)) {
        throw "ログファイルが見つかりません: $LogPath"
    }

    $since   = (Get-Date).AddDays(-$Days).Date
    $pattern = '^(?<date>\d{4}-\d{2}-\d{2})\s+\S+\s+(?<level>\w+)\s+(?<msg>.*)$'
    Write-Verbose "対象: $LogPath ($($since.ToString('yyyy-MM-dd')) 以降)"

    $records = Get-Content -LiteralPath $LogPath -Encoding UTF8 |
        ForEach-Object {
            if ($_ -match $pattern) {
                [PSCustomObject]@{
                    日付   = [datetime]$matches.date
                    レベル = $matches.level
                    内容   = $matches.msg
                }
            }
        } | Where-Object { $_.日付 -ge $since }

    Write-Verbose "解析できた行: $(@($records).Count) 件"

    $records |
        Group-Object { $_.日付.ToString('yyyy-MM-dd') }, { $_.レベル } |
        ForEach-Object {
            [PSCustomObject]@{
                日付   = $_.Group[0].日付.ToString('yyyy-MM-dd')
                レベル = $_.Group[0].レベル
                件数   = $_.Count
            }
        } | Sort-Object 日付, レベル | Format-Table -AutoSize

    $errors = @($records | Where-Object レベル -eq 'ERROR')
    if ($errors.Count -gt 0) {
        Write-Host "ERROR が $($errors.Count) 件あります:" -ForegroundColor Red
        $errors | ForEach-Object { Write-Host "  $($_.日付.ToString('yyyy-MM-dd'))  $($_.内容)" }
    }
    exit 0
}
catch {
    Write-Host "エラー: $($_.Exception.Message)" -ForegroundColor Red
    exit 1
}
つまずき: try/catch に入らない
最も多い詰まりです。Test-Pathエラーを出さず False を返すだけなので、自分で throw する必要があります。
Cmdlet のエラーを捕まえたい場合は -ErrorAction Stop か、上の解答のように先頭で $ErrorActionPreference = 'Stop' を書きます(08.2)。
つまずき: $records.Count がエラーになる
該当行が0件のとき $records$null になり.Count が使えません(06.3)。@($records).Count@() で包むと、0件でも1件でも安全に数えられます。
Set-StrictMode -Version Latest を入れていると、この種のミスがその場でエラーになるので早く気づけます。

全体を通しての助言

状況やること
何が返るか分からない| Get-Member
中身の値を見たい| Select-Object -First 1 | Format-List *
コマンドが思い出せないGet-Command *keyword*
使い方が分からないGet-Help <コマンド> -Examples
0 件しか返らない07.2 の3つの理由を確認する
文字化けする読み書き両方の -Encoding を確認する
エラーが捕まらない-ErrorAction Stop を付ける
この表を手元に置いておいてください。
PowerShell でつまずく場面の大半は、この7行のどれかです。コマンドを暗記する必要はありません。困ったときにどれを試すかを知っていれば、あとは実行環境が教えてくれます。

資料を終えて

到達点
第1部 基礎編01〜05読んで分かる — PowerShell の考え方と文法が理解できる
第2部 実践編06〜09書けるようになる — 自分で関数を作り、実務スクリプトが書ける
第3部 演習10一人で作れる — 課題を通じて手が動くようになる

ここから先の学び方

  1. 身近な手作業を1つ自動化してみる。毎回同じフォルダを開いて同じ操作をしているなら、それが最初の題材です
  2. 詰まったら Get-MemberGet-Help -Examples。この資料に載っていないコマンドも、同じ手順で扱えます
  3. 書いたスクリプトは -WhatIfWrite-Verbose を付けてから使う。事故を防ぎ、後から追えるようになります
05.5 の4ステップが、この資料の要約です。
探す(Get-Command)→ 使い方を見る(Get-Help -Examples)→ 1件だけ動かす → 中身を見る(Get-Member
これが回せれば、資料に書かれていないことも自力で進められます。暗記ではなく調べ方を身につける — それがこの資料の目的でした。