標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

その名も fcopy 、不屈のコピーだ

最近の Windows はファイルのコピーがたいへんだ。

以下のバッチファイルはビルドしたファイルを C:\Program Files\xxxxx にコピーするだけの代物。だが…

  • C:\Program Files\xxxxx に書き込むには管理者権限が必要
  • 管理者モードになるとネットーワークドライブが引き継がれない
    • プロジェクトがネットワークドライブ( 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言語は本当に向いている。

これ一つでほとんどのケースはカバーできるはずだ。

(あと、本当は「特権昇格できない一般ユーザーは、管理者アカウントにスイッチして昇格する」とケースもある。自分はそういうシチュエーションになったことはないのだが、いずれは対応せねばなるまい)