PowerShellでスクリプトを書いていると、「同じことを何度も繰り返したい」という場面によく出会います。
たとえば、フォルダ内のすべてのファイルを処理したり、1から100まで数えたり、条件を満たすまで処理を続けたりしたいときです。
そんなときに使うのが「ループ構文」です。ループ構文を使えば、手作業では時間がかかる繰り返し作業を、プログラムに任せることができます。
PowerShellには、いくつかの種類のループ構文があります。
それぞれに得意な場面があるので、使い分けを覚えると、もっと効率的なスクリプトが書けるようになります。
この記事では、PowerShellの代表的なループ構文を、基本から応用まで丁寧に解説します。
ループの種類と使い分け
PowerShellで使える4つのループ
PowerShellには、主に4つのループ構文があります:
ループの種類 | いつ使う? | 特徴 |
---|---|---|
for | 回数が決まっているとき | カウンターを使って繰り返す |
foreach | 配列やリストの要素を処理するとき | データの数だけ自動で繰り返す |
while | 条件を満たす間続けたいとき | 条件をチェックしてから実行 |
do | 最低1回は実行したいとき | まず実行してから条件をチェック |
どれを選べばいい?
迷ったときの選び方:
- 決まった回数を繰り返したい →
for
ループ - ファイルや配列の中身を順番に処理したい →
foreach
ループ - 条件を満たす間続けたい →
while
ループ - 最低1回は実行してから判断したい →
do
ループ
forループ
どんなもの?
for
ループは、「○回繰り返す」というように、回数が決まっている処理に最適です。カウンターを使って、開始から終了まで順番に処理していきます。
基本の書き方
for (初期値; 条件; 増加値) {
# 実行したい処理
}
実際に使ってみよう
例1:1から5まで数える
for ($i = 1; $i -le 5; $i++) {
Write-Output "現在の数: $i"
}
実行結果:
現在の数: 1
現在の数: 2
現在の数: 3
現在の数: 4
現在の数: 5
例2:カウントダウン
for ($countdown = 10; $countdown -ge 1; $countdown--) {
Write-Output "カウントダウン: $countdown"
Start-Sleep -Seconds 1 # 1秒待つ
}
Write-Output "発射!"
例3:ファイル名を連番で作成
for ($fileNum = 1; $fileNum -le 3; $fileNum++) {
$fileName = "test_file_$fileNum.txt"
New-Item -Path $fileName -ItemType File
Write-Output "$fileName を作成しました"
}
forループの仕組み
- 初期値:
$i = 1
– カウンターの開始値を設定 - 条件:
$i -le 5
– この条件が真の間、繰り返し続ける - 増加値:
$i++
– 毎回、カウンターをどう変更するか
よく使う演算子:
-le
:以下(Less than or Equal)-lt
:未満(Less Than)-ge
:以上(Greater than or Equal)-gt
:より大きい(Greater Than)
実務での活用例
例:複数のサーバーを順番に確認
$serverList = @("server01", "server02", "server03")
for ($i = 0; $i -lt $serverList.Count; $i++) {
$serverName = $serverList[$i]
Write-Output "サーバー $($i + 1): $serverName を確認中..."
# サーバーの状態確認(例)
Test-NetConnection -ComputerName $serverName -Port 80
}
ここがポイント!
- カウンターの名前は
$i
がよく使われますが、$count
や$num
など、わかりやすい名前でもOK - 条件を間違えると、無限ループになることがあるので注意
$i++
は$i = $i + 1
と同じ意味
foreachループ
どんなもの?
foreach
ループは、配列やリストの要素を順番に処理するときに使います。「この箱の中身を、ひとつずつ取り出して処理する」というイメージです。
基本の書き方
foreach ($要素 in $配列) {
# 各要素に対する処理
}
実際に使ってみよう
例1:果物のリストを表示
$fruits = @("りんご", "みかん", "ばなな", "ぶどう")
foreach ($fruit in $fruits) {
Write-Output "好きな果物: $fruit"
}
実行結果:
好きな果物: りんご
好きな果物: みかん
好きな果物: ばなな
好きな果物: ぶどう
例2:ファイル一覧の取得と表示
# 現在のフォルダにあるテキストファイルを取得
$textFiles = Get-ChildItem -Path "*.txt"
foreach ($file in $textFiles) {
$fileSize = [Math]::Round($file.Length / 1KB, 2)
Write-Output "ファイル名: $($file.Name), サイズ: ${fileSize}KB"
}
例3:複数のプロセスを確認
$processNames = @("notepad", "calculator", "explorer")
foreach ($processName in $processNames) {
$process = Get-Process -Name $processName -ErrorAction SilentlyContinue
if ($process) {
Write-Output "$processName は実行中です"
} else {
Write-Output "$processName は実行されていません"
}
}
foreachの便利な機能
パイプラインでも使える:
# こんな書き方もできます
@("ファイル1.txt", "ファイル2.txt", "ファイル3.txt") |
ForEach-Object { Write-Output "処理中: $_" }
ハッシュテーブル(辞書)の処理:
$userInfo = @{
"名前" = "太郎"
"年齢" = "25"
"職業" = "エンジニア"
}
foreach ($key in $userInfo.Keys) {
Write-Output "$key : $($userInfo[$key])"
}
実務での活用例
例:複数のWebサイトの状態確認
$websites = @(
"https://www.google.com",
"https://www.microsoft.com",
"https://www.github.com"
)
foreach ($site in $websites) {
try {
$response = Invoke-WebRequest -Uri $site -TimeoutSec 10
Write-Output "$site : 正常 (ステータス: $($response.StatusCode))"
}
catch {
Write-Output "$site : エラー ($($_.Exception.Message))"
}
}
ここがポイント!
- 配列の要素数を気にしなくていいので、簡潔に書ける
$_
は「現在処理中の要素」を表す特別な変数- ファイルやフォルダの処理によく使われる
whileループ
どんなもの?
while
ループは、「条件を満たしている間、処理を続ける」ループです。何回繰り返すかわからないけれど、ある条件になったら止めたい場合に使います。
基本の書き方
while (条件) {
# 条件が真の間、実行される処理
}
実際に使ってみよう
例1:カウンターを使った基本的な例
$count = 1
while ($count -le 3) {
Write-Output "実行回数: $count"
$count++ # カウンターを増やすのを忘れずに!
}
実行結果:
実行回数: 1
実行回数: 2
実行回数: 3
例2:ファイルができるまで待つ
$filePath = "C:\temp\important_file.txt"
Write-Output "ファイルの作成を待っています..."
while (-not (Test-Path $filePath)) {
Write-Output "まだファイルがありません。5秒後に再確認します..."
Start-Sleep -Seconds 5
}
Write-Output "ファイルが見つかりました!"
例3:ユーザーからの入力待ち
$userInput = ""
while ($userInput -ne "終了") {
$userInput = Read-Host "コマンドを入力してください(終了するには '終了' と入力)"
if ($userInput -eq "時刻") {
Write-Output "現在時刻: $(Get-Date)"
}
elseif ($userInput -eq "日付") {
Write-Output "今日の日付: $(Get-Date -Format 'yyyy年MM月dd日')"
}
elseif ($userInput -ne "終了") {
Write-Output "不明なコマンドです: $userInput"
}
}
Write-Output "プログラムを終了します"
注意:無限ループに気をつけよう
危険な例:
# これは無限ループになります!
$count = 1
while ($count -le 3) {
Write-Output $count
# $count++ を忘れているので、永遠に終わらない
}
安全な書き方:
$count = 1
$maxLoop = 100 # 安全装置
while ($count -le 3 -and $count -le $maxLoop) {
Write-Output $count
$count++
}
実務での活用例
例:サービスの起動を待つ
$serviceName = "Spooler" # プリンタサービス
$maxWaitTime = 60 # 最大60秒待つ
$waitedTime = 0
Write-Output "$serviceName サービスの起動を待っています..."
while ((Get-Service $serviceName).Status -ne "Running" -and $waitedTime -lt $maxWaitTime) {
Write-Output "まだ起動していません... ($waitedTime 秒経過)"
Start-Sleep -Seconds 5
$waitedTime += 5
}
if ((Get-Service $serviceName).Status -eq "Running") {
Write-Output "$serviceName サービスが起動しました!"
} else {
Write-Output "タイムアウト: $serviceName サービスが起動しませんでした"
}
ここがポイント!
- 必ず条件が「偽」になる仕組みを作る
- 無限ループ対策として、最大回数や時間制限を設ける
- ネットワークやファイルの処理でよく使われる
do-while / do-until ループ
どんなもの?
do
ループは、「まず処理を実行してから、条件をチェックする」ループです。
つまり、条件に関係なく、最低1回は必ず実行されます。
2つの種類:
do-while
:条件が真の間、繰り返すdo-until
:条件が偽の間、繰り返す
基本の書き方
# do-while の場合
do {
# 最低1回は実行される処理
} while (条件)
# do-until の場合
do {
# 最低1回は実行される処理
} until (条件)
実際に使ってみよう
例1:do-while の基本
$count = 0
do {
Write-Output "実行回数: $count"
$count++
} while ($count -lt 3)
実行結果:
実行回数: 0
実行回数: 1
実行回数: 2
例2:do-until の基本
$count = 0
do {
Write-Output "実行回数: $count"
$count++
} until ($count -eq 3)
実行結果は同じ:
実行回数: 0
実行回数: 1
実行回数: 2
例3:ユーザー入力の検証
do {
$age = Read-Host "年齢を入力してください(18〜100)"
$ageNumber = [int]$age
if ($ageNumber -lt 18 -or $ageNumber -gt 100) {
Write-Output "18歳以上100歳以下で入力してください"
}
} while ($ageNumber -lt 18 -or $ageNumber -gt 100)
Write-Output "入力された年齢: $age 歳"
whileとdoの違い
while ループ:条件をチェックしてから実行
$condition = $false
while ($condition) {
Write-Output "実行されません" # 1回も実行されない
}
do ループ:実行してから条件をチェック
$condition = $false
do {
Write-Output "必ず1回は実行されます" # 必ず1回実行される
} while ($condition)
実務での活用例
例:設定ファイルの作成
$configPath = "C:\temp\config.ini"
do {
# 設定ファイルを作成する処理
Write-Output "設定ファイルを作成しています..."
try {
@"
[設定]
サーバー名=localhost
ポート=8080
タイムアウト=30
"@ | Out-File -FilePath $configPath -Encoding UTF8
Write-Output "設定ファイルを作成しました"
$created = $true
}
catch {
Write-Output "作成に失敗しました。5秒後に再試行します..."
Start-Sleep -Seconds 5
$created = $false
}
} while (-not $created)
ここがポイント!
- 最低1回は実行したい処理に最適
- ユーザー入力の検証によく使われる
do-while
とdo-until
は条件の意味が逆なので注意
breakとcontinueの使い方
どんなもの?
break
とcontinue
は、ループの流れを制御するための特別なコマンドです。
- break:ループを途中で終了させる
- continue:現在の繰り返しをスキップして、次の繰り返しに進む
breakの使い方
基本例:
for ($i = 1; $i -le 10; $i++) {
if ($i -eq 5) {
Write-Output "5に到達したので終了します"
break
}
Write-Output "現在の数: $i"
}
Write-Output "ループ終了後の処理"
実行結果:
現在の数: 1
現在の数: 2
現在の数: 3
現在の数: 4
5に到達したので終了します
ループ終了後の処理
実用例:エラーが発生したら処理を停止
$files = @("file1.txt", "file2.txt", "important.txt", "file3.txt")
foreach ($file in $files) {
Write-Output "$file を処理中..."
# 重要なファイルに到達したら停止
if ($file -eq "important.txt") {
Write-Output "重要ファイルに到達。安全のため処理を停止します"
break
}
# ファイル処理のシミュレーション
Write-Output "$file の処理が完了しました"
}
continueの使い方
基本例:
for ($i = 1; $i -le 5; $i++) {
if ($i -eq 3) {
Write-Output "3はスキップします"
continue
}
Write-Output "処理中: $i"
}
実行結果:
処理中: 1
処理中: 2
3はスキップします
処理中: 4
処理中: 5
実用例:特定の条件のファイルだけ処理
$files = Get-ChildItem -Path "C:\temp\*.*"
foreach ($file in $files) {
# フォルダはスキップ
if ($file.PSIsContainer) {
Write-Output "$($file.Name) はフォルダなのでスキップ"
continue
}
# サイズが0のファイルもスキップ
if ($file.Length -eq 0) {
Write-Output "$($file.Name) は空ファイルなのでスキップ"
continue
}
# 条件を満たすファイルだけ処理
Write-Output "$($file.Name) を処理します (サイズ: $($file.Length) バイト)"
}
ネストしたループでの使い方
複数のループが入れ子になっている場合、break
とcontinue
はどのループに影響するでしょうか?
for ($i = 1; $i -le 3; $i++) {
Write-Output "外側ループ: $i"
for ($j = 1; $j -le 3; $j++) {
if ($j -eq 2) {
Write-Output " 内側ループの2をスキップ"
continue # 内側ループの次の繰り返しへ
}
if ($i -eq 2 -and $j -eq 3) {
Write-Output " 内側ループを終了"
break # 内側ループを終了
}
Write-Output " 内側ループ: $j"
}
}
実務での活用例
例:ログファイルから特定の情報を抽出
$logFile = "C:\logs\application.log"
$errorCount = 0
$maxErrors = 5
if (Test-Path $logFile) {
$logLines = Get-Content $logFile
foreach ($line in $logLines) {
# 空行はスキップ
if ([string]::IsNullOrWhiteSpace($line)) {
continue
}
# エラーログを発見
if ($line -match "ERROR") {
Write-Output "エラー発見: $line"
$errorCount++
# エラーが多すぎる場合は処理を停止
if ($errorCount -ge $maxErrors) {
Write-Output "エラーが$maxErrors 件に達したため、処理を停止します"
break
}
}
}
}
ここがポイント!
break
:「もうやめる」continue
:「今回はパス、次へ」- ネストしたループでは、最も内側のループに影響する
- エラーハンドリングや条件分岐でよく使われる
実践的な活用例
例1:複数サーバーの監視スクリプト
# 監視対象のサーバーリスト
$servers = @("web01", "web02", "db01", "app01")
$checkInterval = 30 # 30秒間隔
$maxRetries = 3
Write-Output "サーバー監視を開始します..."
# 無限ループで監視継続
while ($true) {
Write-Output "$(Get-Date): 監視開始"
foreach ($server in $servers) {
$retryCount = 0
$isOnline = $false
# 各サーバーを最大3回まで試行
do {
$retryCount++
Write-Output " $server を確認中 (試行: $retryCount)"
$pingResult = Test-NetConnection -ComputerName $server -Port 80 -WarningAction SilentlyContinue
if ($pingResult.TcpTestSucceeded) {
Write-Output " ✓ $server はオンラインです"
$isOnline = $true
break
} else {
Write-Output " ✗ $server に接続できません"
if ($retryCount -lt $maxRetries) {
Write-Output " 5秒後に再試行します..."
Start-Sleep -Seconds 5
}
}
} while ($retryCount -lt $maxRetries -and -not $isOnline)
# 最終的に接続できない場合
if (-not $isOnline) {
Write-Output " ⚠️ $server への接続に失敗しました($maxRetries 回試行)"
}
}
Write-Output "次の監視まで $checkInterval 秒待機..."
Start-Sleep -Seconds $checkInterval
}
例2:ファイル整理の自動化
# 整理対象のフォルダ
$sourceFolder = "C:\Downloads"
$photoFolder = "C:\Photos"
$documentFolder = "C:\Documents"
# 写真とドキュメントの拡張子
$photoExtensions = @(".jpg", ".jpeg", ".png", ".gif", ".bmp")
$documentExtensions = @(".pdf", ".docx", ".xlsx", ".pptx", ".txt")
Write-Output "ファイル整理を開始します..."
# フォルダ内のファイルを取得
$files = Get-ChildItem -Path $sourceFolder -File
if ($files.Count -eq 0) {
Write-Output "整理対象のファイルがありません"
} else {
Write-Output "$($files.Count) 個のファイルを確認します"
foreach ($file in $files) {
$extension = $file.Extension.ToLower()
$destinationFolder = $null
# ファイルの種類を判定
if ($photoExtensions -contains $extension) {
$destinationFolder = $photoFolder
$fileType = "写真"
}
elseif ($documentExtensions -contains $extension) {
$destinationFolder = $documentFolder
$fileType = "ドキュメント"
}
else {
Write-Output " $($file.Name) : 対象外のファイル形式です"
continue
}
# 移動先フォルダが存在しない場合は作成
if (-not (Test-Path $destinationFolder)) {
New-Item -Path $destinationFolder -ItemType Directory -Force
Write-Output " フォルダを作成しました: $destinationFolder"
}
# ファイルを移動
try {
$destinationPath = Join-Path $destinationFolder $file.Name
Move-Item -Path $file.FullName -Destination $destinationPath
Write-Output " ✓ $($file.Name) を $fileType フォルダに移動しました"
}
catch {
Write-Output " ✗ $($file.Name) の移動に失敗: $($_.Exception.Message)"
}
}
}
Write-Output "ファイル整理が完了しました"
例3:定期的なバックアップスクリプト
# バックアップ設定
$sourceFolder = "C:\ImportantData"
$backupBaseFolder = "D:\Backups"
$maxBackups = 7 # 保持するバックアップ数
$backupInterval = 3600 # 1時間間隔(秒)
Write-Output "自動バックアップシステムを開始します..."
while ($true) {
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupFolder = Join-Path $backupBaseFolder "Backup_$timestamp"
Write-Output "$(Get-Date): バックアップを開始します"
try {
# バックアップフォルダを作成
New-Item -Path $backupFolder -ItemType Directory -Force | Out-Null
# ファイルをコピー
$sourceFiles = Get-ChildItem -Path $sourceFolder -Recurse -File
$totalFiles = $sourceFiles.Count
$currentFile = 0
foreach ($file in $sourceFiles) {
$currentFile++
# 進捗表示
if ($currentFile % 10 -eq 0 -or $currentFile -eq $totalFiles) {
$progress = [Math]::Round(($currentFile / $totalFiles) * 100, 1)
Write-Output " 進捗: $progress% ($currentFile/$totalFiles)"
}
# 相対パスを保持してコピー
$relativePath = $file.FullName.Substring($sourceFolder.Length + 1)
$destinationPath = Join-Path $backupFolder $relativePath
$destinationDir = Split-Path $destinationPath -Parent
if (-not (Test-Path $destinationDir)) {
New-Item -Path $destinationDir -ItemType Directory -Force | Out-Null
}
Copy-Item -Path $file.FullName -Destination $destinationPath
}
Write-Output " ✓ バックアップが完了しました: $backupFolder"
# 古いバックアップを削除
$existingBackups = Get-ChildItem -Path $backupBaseFolder -Directory |
Where-Object { $_.Name -like "Backup_*" } |
Sort-Object CreationTime -Descending
if ($existingBackups.Count -gt $maxBackups) {
$backupsToDelete = $existingBackups | Select-Object -Skip $maxBackups
foreach ($oldBackup in $backupsToDelete) {
Remove-Item -Path $oldBackup.FullName -Recurse -Force
Write-Output " 古いバックアップを削除: $($oldBackup.Name)"
}
}
}
catch {
Write-Output " ✗ バックアップに失敗: $($_.Exception.Message)"
}
Write-Output "次のバックアップまで $($backupInterval / 60) 分待機..."
Start-Sleep -Seconds $backupInterval
}
まとめ
PowerShellのループ構文は、繰り返し処理を効率的に行うための強力な機能です。
それぞれの特徴を理解して、適切に使い分けることが大切です。
使い分けのまとめ
構文 | こんなときに使う | メリット |
---|---|---|
for | 決まった回数の繰り返し | シンプルで分かりやすい |
foreach | 配列やファイルリストの処理 | 要素数を気にしなくていい |
while | ユーザー入力の検証 | 最初の一回は必ず実行 |
コメント