【自動化】複数のExcelファイルの 特定のセルに 特定の文字を入れる

プログラミング未経験でもできる（はず）

※当記事に登場する人名地名等はすべて架空です。実在の人物地名その他いかなる個人情報とも関係ありません。
※※当記事は随時サイレント修正しています。コードの修正や重大なバグがあった場合のみお知らせいたします。

## あ～っ！レビュー記録票フォーマットの誤字に工程の終わりごろ気づいたよ～

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/87aa513a-b116-f1f9-6798-0963b81c57d4.png)
ひとつひとつ直すにも、誤字フォーマットを使ったファイルが無数にあるため、かなり手がかかる状況です。

## やりたいこと
複数のExcelファイルのB2セルに「レビュー記録票」って入れたい

## どうやる

### ① ファイルを2つ作る

下記コードをメモ帳にコピーして、指定の通りに名前を付けて保存します。

```batchfile:特定のセルに特定の文字を入力.bat
powershell -NoProfile -ExecutionPolicy Unrestricted "./特定のセルに特定の文字を入力.ps1" >> 特定のセルに特定の文字を入力.log
exit
```
```powershell:特定のセルに特定の文字を入力.ps1
$dir = Convert-Path .
$regex = ".*\.xl(s|t|a|w|r|sx|sm|sb|tx|tm|am)$"
$filePathList = Get-ChildItem $dir -File | Where-Object { $_.Name -match $regex } | ForEach-Object { $_.FullName }

#　アセンブリの読み込み インプットボックス表示に必要
[void][System.Reflection.Assembly]::Load("Microsoft.VisualBasic, Version=8.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")
Add-Type -Assembly System.Windows.Forms


function Log($str) {
	$timestamp = Get-Date
	Write-Host "$timestamp $str"
}


try {

	Log "********* 特定のセルに特定の文字列を入力.ps1 起動 ***********"

	if ($filePathList.Length -lt 1) {
        $msg = "対象ファイルがありません。"
		[System.Windows.Forms.MessageBox]::Show($msg, '情報', 'OK', 'Information') | Out-null
		log $msg
		exit
	}

	#宣言
	$targetSheetStr = ""
	$targetCellStr = ""
	$targetTxt = ""

	try {
		#　インプットボックスの表示
		$targetSheetStr = [Microsoft.VisualBasic.Interaction]::InputBox("対象とするシートを入力してください。すべての場合は「all」と入力", "ダイヤログ")
		$targetCellStr = [Microsoft.VisualBasic.Interaction]::InputBox("対象とするセルを入力してください", "ダイヤログ")
		$targetTxt = [Microsoft.VisualBasic.Interaction]::InputBox("入力したい文字列を入力してください", "ダイヤログ")
	}
	catch {
	    Log "インプットボックスでエラーが発生しました。"
		throw
	}
	
	$msgBoxTxt = "次の通り実行します。よろしいですか？対象シート：$targetSheetStr 対象セル：$targetCellStr 入力文字列：$targetTxt"
	
	$msgBoxInput = [System.Windows.Forms.MessageBox]::Show($msgBoxTxt,'アプリの実行確認','YesNo','Question')
	
	if($msgBoxInput -ne "Yes") {
		Log "ユーザー入力により処理を中止します。"
		Exit
	}
	
	Log "対象シート：$targetSheetStr 対象セル：$targetCellStr 入力文字列：$targetTxt"
	
	
	# エクセルを開く準備
	$excel = New-Object -ComObject Excel.Application
	$excel.Visible = $false
	$excel.DisplAyalerts = $false
		
	$filePathList | ForEach-Object{

	    $filePath = $_
	   	$updateLinks = 0
   		$isReadOnly = $false
	
		try {
			#エクセルを開く
			$book = $excel.Workbooks.Open($filePath, $updateLinks, $isReadOnly)
            $bookName = $book.Name
		}
		catch {
			Log "エクセルのオープンでエラーが発生しました。"
			throw
		}
		
		Log "$bookName　を編集中..."
	
		$cnt = 0 #処理したかどうかを判定する。対象シートがなければ0になる
	
		try {
			$book.Sheets | ForEach-Object{
				$sheet = $_
				   if (($targetSheetStr　-eq "all") -or ($targetSheetStr -eq $sheet.name)) {
						$sheet.Activate()
						$sheet.Range($targetCellStr) = $targetTxt
						$excel.ActiveWindow.Zoom = 100
						$sheet.Cells.Item(1, 1).Select() | Out-Null
					
                        $cnt += 1
                    }
			}
		}
		catch [System.MissingMemberException]{
            $msg = "対象セル：$targetCellStr は無効です。初めからやり直してください。"
			[System.Windows.Forms.MessageBox]::Show($msg,'エラー', 'OK', 'Error') | Out-null
			Log $msg
            throw
		}
		catch {
			Log "シートのオープンまたはセルへの入力でエラーが発生しました。"
			throw
		}
		
		if ($cnt -eq 0) {
			$book.Close($false)
			Log "$bookName：対象シートがありませんでした。保存せずにブックを閉じました。"
		}
		else {
			try {
				$book.Sheets.Item(1).Activate()
				$book.Save()
				$isSaveChanges = $true
				$book.Close($isSaveChanges)
				Log "$bookName：$cnt 件のシートを更新し、保存しました。"
            }
			catch {
				Log "ファイルの保存でエラーが発生しました。"
				throw
			}
		}

    } #$filePathList | ForEach-Object ここまで

}
catch {
	$errorStr = $error[0] | Out-String
	Log $errorStr
	Log "エラーが発生したため、処理を終了します。"
}
finally {
    try {
		if ($excel -ne $null) {
			$excel.Quit()
		}
		$sheet = $null
		$book = $null
		$excel = $null
		[System.GC]::Collect()
	}
	catch {
		Log "finally処理の中でエラーが発生しました。"
		$errorStr = $error[0] | Out-String
		Log $errorStr
    }
	Log "********* 特定のセルに特定の文字列を入力.ps1 終了 ***********"
	pause
}
exit
```

### ② ①で作ったファイルと同じフォルダに編集したいExcelファイルを入れる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/92260366-87f3-e01e-59f3-e32cb118bef7.png)

### ③ 特定のセルに特定の文字を入力.bat をダブルクリックで実行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/93dc3461-4903-900f-859c-beff2f868b3e.png)
ダイヤログが出てくるので指示の通りに入力します。
今回は「レビュー記録」シートだけ編集しますが、all指定もできます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/5bd14f66-9780-6900-dd65-ea7179bca6af.png)
文字を入力するセルを指定します。
B2,D2って指定するとB2とD2に文字が入ります。
B2:D2って指定するとB2,C2,D2に文字が入ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/8a3e280c-d18f-52f2-ae8b-891a55fcd3a3.png)
入れたい文字を指定します。何も入れないと空文字になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/ddf78d46-4e4a-fd5a-4f86-e52f9653f602.png)
実行確認で「はい」を押すと処理が開始します。

:::note warn
処理中はPCを操作しないでください。
:::


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/e170e92d-e385-a574-55fc-1f0543f6163a.png)

この表示になったら処理終了です。Enterキーを押してください。

### ④実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3505596/7fc96ed9-baa8-d868-5495-1f9561631c70.png)
ちゃんと直っていました。
ちなみにこのエクセルファイルには「setting」というシートもあるのですが、
今回は「レビュー記録」だけを対象に指定したため、「setting」のほうは無傷です。


## 課題
- __使う場面がそんなにない__
- 文字入力ダイヤログはSystem.Windows.Formsにもあったので、そっちを使ってもよかった

## 参考資料

https://letspowershell.blogspot.com/2015/06/powershellinputbox.html

http://powershell.web.fc2.com/Html/Data/2012/12/12/0000.html

http://kamifuji.dyndns.org/PS-Support/PS_Excel/index.html

https://qiita.com/miyamiya/items/161372111b68bad0744a

https://qiita.com/ishi720/items/bac74397ec3521f8f868