最近の Windows はファイルのコピーがたいへんだ。
以下のバッチファイルはビルドしたファイルを C:\Program Files\xxxxx
にコピーするだけの代物。だが…
C:\Program Files\xxxxx
に書き込むには管理者権限が必要- PowerShell で管理者モードに昇格
- 管理者モードになるとネットーワークドライブが引き継がれない
- プロジェクトがネットワークドライブ(
net use Z: \\vmware-host\Share Folders\...
上) にあるので外されると困るので、管理者モードになってからnet use
コマンドを発行しなおす。
- プロジェクトがネットワークドライブ(
といったことをがんばってやっている。
@setlocal @setn "prompt=[%~n0] " set "TARGETNAME=TARGET.exe" @set "TARGET=%~dp0.\Release\%TARGETNAME%" @if not exist "%TARGET%" set "TARGET=%~dp0.\%TARGETNAME%" set "TARGET=%TARGET%" set "INSTALL=C:\Program Files\xxxxxxx" @call :"%1" @endlocal @exit /b :"_install" copy /v "%TARGET%" "%INSTALL%" pause exit /b :"" powershell "start-process -FilePath 'cmd.exe' -ArgumentList ('/s /c '+[char]34+((get-wmiobject win32_networkconnection | %%{ 'net use '+$_.LocalName+' '+[char]34+$_.RemoteName+[char]34 }) -join ' & ') + ' & ' + [char]34 + '%~dpnx0' + [char]34 + ' _install' + [char]34) -verb 'runas'" exit /b
他の例として、使用中の nyagos.exe を置き換える時、そのままだと「他のプロセスが使用中です」となってしまう。そのため、
- コピーに失敗した時は、上書き先のファイルを別の名前にリネームして(他のプロセスが使用していてもリネームはできたりする)、それから改めて再コピー
といったことを PowerShell でやってたりする。
function Do-Copy($src,$dst){ Write-Verbose "$ copy '$src' '$dst'" Copy-Item $src $dst } function Do-Rename($old,$new){ Write-Verbose "$ ren $old $new" Rename-Item -path $old -newname $new } 中略 try{ Do-Copy nyagos.exe $installDir }catch{ $old = (Join-Path $installDir "nyagos.exe") Write-Warning "Failed to update $old" $now = (Get-Date -Format "yyyyMMddHHmmss") $backup = ($old + "-" + $now) try{ try{ Do-Rename $old $backup }catch{ Write-Warning "Failed to rename $old to $backup" Write-Warning "Try to kill nyagos.exe process" taskkill /F /IM nyagos.exe Do-Rename $old ($old + "-" + $now) } Do-Copy nyagos.exe $installDir }catch{ Write-Error "Could not update installed nyagos.exe" Write-Error "Some processes holds nyagos.exe now" } }
こういうのを今までずっとバッチファイルとか PowerShell でやってきたんだけど、いい加減たいへんだ。 あんまり汎用化していないので、Visual Studio のプロジェクトごとにいちいちコピーして修正していたりするし、多くの処理を1行PowerShel の中に詰め込んだりしている箇所もあって、保守がしづらい。
そこで「コピー先がどこであろうと、がんばってコピーするプログラム」を Go 言語で作った。 その名も fcopy 、不屈のコピーだ。
文法的には cp とだいたい同じ。
$ fcopy ソースファイル名1 ソースファイル名2 ... 宛先フォルダー OR $ fcopy ソースファイル名 コピー先ファイル名
このコピーは、上書きするファイル名が使用中でも、管理者権限が必要なフォルダーでも、うまいことアレしてくれる (上書きできないファイルをリネームしたり、UACダイアログを出したりする)。
こういう OS の API を直接呼びたいし、その上ロジックがいろいろ面倒で C言語ではしんどい…といった用途にはGo言語は本当に向いている。
これ一つでほとんどのケースはカバーできるはずだ。
(あと、本当は「特権昇格できない一般ユーザーは、管理者アカウントにスイッチして昇格する」とケースもある。自分はそういうシチュエーションになったことはないのだが、いずれは対応せねばなるまい)