Win10のhostsをBATで書き換え

## 目的

WordPressで構築したサイトをドメインはそのままでサーバー移行する際、そのサイトの管理者に移行先の動作を確認してもらう必要があった。

管理者のスキルだとhostsの直接編集が難しそうだったためhostsの切り替えを簡単に行うことを目指す。

## 検討

まずは検索
[windows hosts 編集 バッチ - Google 検索](https://www.google.com/search?q=windows+hosts+%E7%B7%A8%E9%9B%86+%E3%83%90%E3%83%83%E3%83%81&rlz=1C1GCEU_jaJP822JP822&oq=windows+hosts+%E7%B7%A8%E9%9B%86+%E3%83%90%E3%83%83%E3%83%81&aqs=chrome..69i57.15291j0j7&sourceid=chrome&ie=UTF-8)

[Windows 10でhostsファイルを編集する方法と、一発で編集できる便利コマンド | TeraDas](https://www.teradas.net/archives/24455/)

```bat
powershell -NoProfile -ExecutionPolicy unrestricted -Command "start notepad C:\Windows\System32\drivers\etc\hosts -verb runas"
```

[Windowsのhostsファイルの変更がめんどくさい人のためのbat - Qiita](https://qiita.com/koginoadm/items/b81690771b47acfcfebe)
[djeeno.log: Windowsでhostsをすごく簡単に編集するためのバッチファイル(.bat)](https://djeeeno.blogspot.com/2018/01/20180127-01-bat-windows-edit-hosts.html)

同じ内容が紹介されていた。

```bat
@echo off
start powershell.exe -Command "Start-Process -Verb RUNAS notepad C:\Windows\System32\drivers\etc\hosts"
```

[maruton's memorandum: ［Windows］hostsファイルを変更するバッチファイル](http://maruton-memorandum.blogspot.com/2017/12/windowshosts.html)

```bat
set hosts=c:\windows\system32\drivers\etc\hosts
set hoststmp=c:\windows\system32\drivers\etc\hosts.tmp
copy %hosts% %hoststmp%
notepad %hoststmp%
copy %hoststmp% %hosts%
pause
```

[Powershell to manipulate host file - Stack Overflow](https://stackoverflow.com/questions/2602460/powershell-to-manipulate-host-file/11353979)
上記ページで下のスクリプトが紹介されていた
[Powershell script for adding/removing/viewing entries to the hosts file.](https://gist.github.com/markembling/173887)

```hosts.ps1
#
# Powershell script for adding/removing/showing entries to the hosts file.
#
# Known limitations:
# - does not handle entries with comments afterwards ("<ip>    <host>    # comment")
#

$file = "C:\Windows\System32\drivers\etc\hosts"

function add-host([string]$filename, [string]$ip, [string]$hostname) {
	remove-host $filename $hostname
	$ip + "`t`t" + $hostname | Out-File -encoding ASCII -append $filename
}

function remove-host([string]$filename, [string]$hostname) {
	$c = Get-Content $filename
	$newLines = @()
	
	foreach ($line in $c) {
		$bits = [regex]::Split($line, "\t+")
		if ($bits.count -eq 2) {
			if ($bits[1] -ne $hostname) {
				$newLines += $line
			}
		} else {
			$newLines += $line
		}
	}
	
	# Write file
	Clear-Content $filename
	foreach ($line in $newLines) {
		$line | Out-File -encoding ASCII -append $filename
	}
}

function print-hosts([string]$filename) {
	$c = Get-Content $filename
	
	foreach ($line in $c) {
		$bits = [regex]::Split($line, "\t+")
		if ($bits.count -eq 2) {
			Write-Host $bits[0] `t`t $bits[1]
		}
	}
}

try {
	if ($args[0] -eq "add") {
	
		if ($args.count -lt 3) {
			throw "Not enough arguments for add."
		} else {
			add-host $file $args[1] $args[2]
		}
		
	} elseif ($args[0] -eq "remove") {
	
		if ($args.count -lt 2) {
			throw "Not enough arguments for remove."
		} else {
			remove-host $file $args[1]
		}
		
	} elseif ($args[0] -eq "show") {
		print-hosts $file
	} else {
		throw "Invalid operation '" + $args[0] + "' - must be one of 'add', 'remove', 'show'."
	}
} catch  {
	Write-Host $error[0]
	Write-Host "`nUsage: hosts add <ip> <hostname>`n       hosts remove <hostname>`n       hosts show"
}
```

## 結果

[hosts.ps1](https://gist.github.com/markembling/173887) が扱いやすそうだったのでこれをBATから起動する方針に決定。

下のBATを、「移行先に切り替え」、「移行元に切り替え」、「設定削除（移行期間完了時に使用）」の三種類に適宜書き換え、hosts.ps1と同じフォルダに置く。BATを「管理者として実行」してhostsを編集する。

```bat
@echo off
REM 設定の追加 ファイル実行の引数に「 add 」、「 ipアドレス 」、「 ホスト名 」を追加
@powershell %~dp0hosts.ps1 add 127.0.0.1 example.com -Verb runas
REM 設定の削除 ファイル実行の引数に「 remove 」と「 ホスト名 」を追加
@powershell %~dp0hosts.ps1 remove example.com -Verb runas
REM スクリプト実施後の設定値一覧を表示
echo result: 
@powershell -ExecutionPolicy RemoteSigned -File %~dp0hosts.ps1 show
REM 念のため DNS リゾルバー キャッシュ削除
ipconfig /flushdns
pause
```

## その他参考
* [【PowerShell】別の.ps1スクリプトや外部プログラムを引数付きで呼び出す | ぴぐろぐ](https://pig-log.com/powershell-call-program/)
* [windows10のhostsファイル書き換え方と効かない時の対処方法 – AMAZON AWS/Microsoft Azure/WordPress | ニシインターナショナル](https://4649-24.com/windows10/hosts-file-rewrite/)
* [windowsでhostsを変更後、設定が反映されないときのメモ - seindex](http://www.seindex.net/2016/12/windowshosts.html)

## 備考
hosts.ps1 の print-hosts の関数名が原因で UseApprovedVerbs [PSScriptAnalyzer/UseApprovedVerbs.md at master · PowerShell/PSScriptAnalyzer · GitHub](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/RuleDocumentation/UseApprovedVerbs.md) の警告がテキストエディタ上に出たので、
[PowerShell コマンドの承認された動詞 - PowerShell | Microsoft Docs](https://docs.microsoft.com/ja-jp/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands?view=powershell-6)
で許されている動詞に変更して使っている。

```diff:hosts.ps1
- 37 function print-hosts([string]$filename) {
+ 37 function show-hosts([string]$filename) {

- 66 		print-hosts $file
+ 66 		show-hosts $file
```

```試行錯誤メモ.bat
REM 管理者権限パスのための引数検討
@powershell -ExecutionPolicy RemoteSigned -File %~dp0hosts.ps1 remove centralltd.co.jp -Verb runas

REM 最初の検討→引数のつけ方がよくわからなかった
@powershell -Command "Start-Process powershell.exe -ArgumentList c:\temp\hosts.ps1 -Verb runas"

REM 子プロセスのスクリプト空の返り値検討
@powershell -Command "Start-Process powershell.exe -ArgumentList %~dp0test.ps1 -Verb runas;exit $LASTEXITCODE"
echo Powershell returned value: %ERRORLEVEL%
```