Powershellのスクリプトモジュールの使い道【.csprojの作成]

コンピュータ
PowerShellのあれこれ

Powershelllのスクリプトモジュールという用語は知ってはいましたが、いまいちピンとくる使い道が思いつきませんでした。
早々使い回す関数やクラスなど書いたりしませんし、またライブラリとして再利用することを意識したコーディングは息が詰まります。
多人数で開発するようなプロジェクトなどであれば、モジュール化することでプロジェクト内で共通関数やクラスを使う場面もあるかとは思いますが、基本個人で作成するプログラムでそれほど大きな代物は皆無な状況です。

とまぁスクリプトモジュールをスクリプトで再利用するライブラリとして考えていましたので、敷居の高い代物と避けてきました。

時にPowershellのスクリプトファイルである.ps1はダブルクリックで直接実行することが出来ません。Powershellの前身?であるコマンドプロンプトの.batや.cmdは直接実行することが出来ます。回避策としてvbsでラップしてあげるなどの方法もありますが、セキュリティを担保するためだとしてもちょっと使いづらいです。

エクスプローラからダブルクリックは無理にしても、パスが切られたディレクトリにある.batや.cmdの様に実行属性のあるファイルの様にPowershellからはパスを省略してスクリプト名だけで実行することが出来れば少しは使いやすく成ります。

というわけで、スクリプトモジュールの出番になります。サンプルとして以前作成したC#のプロジェクトを作成するスクリプトをスクリプトモジュール化しました。

このスクリプトはC#のプロジェクトを作成するごとに実行していますが、ほぼ単独で実行する外部コマンド(Powershell後でコマンドレット)みたいな使い方になります。ls(Get-ChildItem)やcd(Set-Location)的な使い方をする、良く使うオリジナルな機能が欲しい場合スクリプトモジュールをつくることで、ちょっとシェルからのスクリプトの実行が楽になります。

スクリプト

保存先:c:/Users/ユーザー名/documents/WindowsPowerShell/Modules/New-Csproj/New-Csproj.psm1

<#
.SYNOPSIS
 .csprojを新規作成

.EXAMPLE
 New-Csproj -ProjectName プロジェクト名 [-Target:winexe|exe] [-Outdir:"."]

#>


Set-StrictMode -Version Latest
$ErrorActionPreference = "STOP"

function New-Csproj
{
    param(
        [Parameter(ValueFromPipeline=$false,Mandatory=$true)]
        [string]
        $ProjectName,
        [Parameter(ValueFromPipeline=$false,Mandatory=$false)]
        [string]
        $Target = "winexe",
        [Parameter(ValueFromPipeline=$false,Mandatory=$false)]
        [string]
        $Outdir = (Resolve-Path(".")).Path

    )

$cspoj_temp = @'
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>  
   <AssemblyName></AssemblyName>
    <OutputPath>Bin\</OutputPath>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
  </PropertyGroup>  
  <ItemGroup>
    <Compile Include="*.cs"/>
  </ItemGroup>
  <Target Name="Build" Inputs="@(Compile)" Outputs="$(OutputPath)$(AssemblyName).exe">
    <MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
    <Csc Sources="@(Compile)" TargetType="" OutputAssembly="$(OutputPath)$(AssemblyName).exe"/>
  </Target>
  <Target Name="Clean" >  
    <Delete Files="$(OutputPath)$(AssemblyName).exe" />
  </Target>
  <Target Name="Rebuild" DependsOnTargets="Clean;Build" />
  <Target Name="Run" DependsOnTargets="Rebuild">
    <Exec Command="$(OutputPath)$(AssemblyName).exe" />
  </Target>
</Project>
'@

# アプリケーションのソースファイルを作成
$consol_app_temp = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ${projectname}
{
    class Program
    {
        // アプリケーションのエントリポイント
        static void Main(string[] args)
        {
            Console.Write("${projectname}");
        }
    }
}
"@

$form_app_temp = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace ${projectname}
{
    class Program
    {
        // アプリケーションのエントリポイント
        [STAThread]
        static void Main(string[] args)
        {
            using (var form = new Form1())
            {
                Application.Run(form);
            }
        }
    }
}
"@

$form_form_temp = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace ${projectname}
{
    class Form1 : Form
    {
        // コンストラクタ
        public Form1()
        {
            this.Load += Form1_Load;
        }

        // リソースの解放
        protected override void Dispose (bool disposing)
        {
            base.Dispose(disposing);
        }

        // Loadイベント
        private void Form1_Load(object sender, EventArgs e)
        {
            // タイトルをセット
            this.Text = "${projectname}";
        }
    }
}
"@

    # プロジェクト用ディレクトリの作成
    $projdir = Join-Path $Outdir $ProjectName
    if (-not(Test-Path($projdir))) {
        mkdir $projdir | Out-Null
    }

    # csprojの作成
    $local = Join-Path $projdir ("{0}.csproj" -f $ProjectName)
    $xml = [xml]$cspoj_temp

    $xml.Project.PropertyGroup.AssemblyName = $projectname
    $xml.Project.Target | ? {$_.Name -eq "Build"} | % {
        $_.Csc.TargetType = $Target
    }
    $xml.Save($local)


    $source_path = Join-Path $projdir ("{0}.cs" -f $projectname)

    if ($Target -eq "exe") {
        $source = $consol_app_temp
        $source | %{[Text.Encoding]::UTF8.GetBytes($_)} | Set-Content $source_path -Encoding Byte
    }
    if ($Target -eq "winexe") {
        $source = $form_app_temp
        $source | %{[Text.Encoding]::UTF8.GetBytes($_)} | Set-Content $source_path -Encoding Byte

        $source_path = Join-Path $projdir "From1.cs"
        $source = $form_form_temp
        $source | %{[Text.Encoding]::UTF8.GetBytes($_)} | Set-Content $source_path -Encoding Byte
    }


}

Export-ModuleMember -Function New-Csproj

使い方

パスを省略出来ます。

覚書

モジュールを修正した場合、実行するpowershell.exeやpowershell_ise.exeを再起動しないとモジュールが適用されないようだ。

コメント