2017/12/10

この記事はPowerShell Advent Calendar 2017の10日目です。

PowerShellはオブジェクトを扱うシェルですが、別にテキストデータを扱えない訳ではありません。むしろ、PowerShellで取得したデータをテキストファイルとして保存したり、スクリプトで用いるデータをテキストファイルで保存しておくことは日常的に行われることだと思います。

ただし、PowerShellで扱うデータはオブジェクトであり、テキストファイルは文字通り文字列であることから、コマンドレットを用いる等、何らかの手段で変換が必要になります。また、テキストデータ形式にも様々な種類があり、それぞれメリット、デメリットが存在します。今回の記事では、PowerShellで用いるデータを保持しておく際のテキストデータ形式について比較をしてみます。

プレーンテキスト

プレーンテキスト、すなわち書式なしのテキストファイルです。もっともシンプルな使い方をする場合、文字列配列の各要素に含まれる文字列が、テキストファイルの1行と対応します。

書き出し
$lines | Set-Content -LiteralPath file.txt -Encoding UTF8

$linesは文字列変数です。

特に理由がなければ文字コードはUTF-8で良いと思います。

追記
$lines | Add-Content -LiteralPath file.txt -Encoding UTF8

Add-Contentは実行のたびにファイルを開いて、書き込んでから閉じるという動作をするので、1行ずつforeachで実行するのはNGです。

読み込み
$lines = @(Get-Content -LiteralPath file.txt -Encoding UTF8)
メリット
  • 文字列配列をテキストファイルに書き出すのは多分これが一番楽だと思います。
  • 書き出したデータは人間にも読みやすい。 編集もしやすい。
デメリット
  • 文字列だけを保存しておきたいというケースがそもそも少ない。
CSV

コンマ等の特別な文字で区切り、1行あたりに複数のデータを保存できる形式です。

PowerShellのコマンドレットで扱う場合、オブジェクトが持つプロパティがヘッダ列名に対応します。各行にオブジェクト配列1要素のプロパティ値が、カンマ区切りで保持されます。

書き出し
$objects | Export-Csv -LiteralPath file.csv -NoTypeInformation -Encoding Default

$objectsは任意のオブジェクト配列です。必要であればSelect-Objectコマンドレットを併用して、プロパティを絞り込みます。

文字コードはExcelでそのまま読み込み/書き出しができるDefault(日本語環境ではShift_JIS)がお勧めです。(最近のExcel2016ならUTF8も一応読めますが)

追記
$objects | Export-Csv -LiteralPath file.csv -Append -NoTypeInformation -Encoding Default
読み込み
$objects = Import-Csv -LiteralPath file.csv -Encoding Default
メリット
  • オブジェクトのプロパティ値が、すべて数値あるいは文字列で表現できる値を持つ場合に最も適合する。
  • 人間にも読みやすく、ある程度は編集もできる。
  • Excelで開ける。
デメリット
  • オブジェクトのプロパティが、数値と文字列以外のオブジェクトである場合、すなわち、階層構造を持つデータの保存には適さない。
  • 数値も文字列として読み込まれてしまうので、数値として扱いたい場合は変換が必要になる。
  • Export-CsvとImport-Csvで扱うCSVファイルはヘッダが必須。つまり、ヘッダなしのCSVファイルが既にあって、それを読み書きするという用途には適さない。(できなくはないが)
  • 書き出し時の列順を制御することができない。つまり、PowerShellで書き出したCSVを、列順が固定であるとの想定である他のプログラムで読み込むことは基本NG。
  • 書き出し時、1つ目の要素に存在しないプロパティは、2つ目以降では存在しないものとして扱われる。同種のオブジェクトで構成される配列なら通常は問題ないのだが、要素によって動的に追加されるプロパティがあったりなかったりすると厄介。(ADでありがち)
JSON

JavaScriptのような表記でデータを保持するデータ形式です。データの受け渡しに様々な言語で利用できます。Web APIでもよく利用されます。

PowerShellではv3からJSONを扱うコマンドレットが提供されています。

書き出し
$objects | ConvertTo-Json | Set-Content -LiteralPath file.json -Encoding UTF8
読み込み
$objects = Get-Content -LiteralPath file.json -Encoding UTF8 -Raw | ConvertFrom-Json
メリット
  • CSVと異なり、階層構造を持ったデータでも扱える。
  • CSVと異なり、数値は数値型のまま読み書き可能。 (整数値はint、小数値はdecimal)
  • 人間にもまぁまぁ読めるし、頑張れば編集できなくもない。
デメリット
  • -Depthパラメータによりプロパティを展開する階層の深さを指定はできるが、プロパティに応じて深さ指定を変化させるというようなことはできない。基本的には、自分で構築したPSCustomObjectを使うか、JSON化する前に自分で元オブジェクトを整形しておく必要がある。
  • 直接ファイルに書き出し、追記、ファイルから読み込みするコマンドレットはない。
  • 実は細かい話をしだすと色々と罠があります…。
CLIXML

PowerShellではPSリモーティング等、プロセス間でオブジェクトのやり取りを行う際に、CLIXML形式を介してシリアライズ/デシリアライズが実行されます。シリアライズ対象によっては、完全に元のクラスのオブジェクトに復元されます。(復元されないオブジェクトにはクラス名にDeserialized.との接頭辞が付与され、プロパティ値のみ復元される)

ユーザーもコマンドレットを用いて、任意のデータをCLIXML形式でシリアライズし、XMLファイルとして保存することができます。

書き出し
$objects | Export-Clixml -LiteralPath file.xml
読み込み
$objects = Import-Clixml -LiteralPath file.xml
メリット
  • 元のオブジェクトの構造、プロパティ値と型情報を含めてほぼ完全にテキストファイルに保存できる。
  • 復元したオブジェクトはプロパティ値を参照できるのはもちろん、オブジェクト全体が完全にデシリアライズされ、元の型に戻った場合には、メソッドを実行することも可能。
  • 例え元の型に戻らず、Deserialized.との接頭辞が付いた状態でも、コンソールに表示する場合は元の型のフォーマットが使われるので見やすい。
デメリット
  • すべてのオブジェクトが元の型に戻せるわけではない。戻せるかどうかは確認が必要。
  • 人間が読み書きするようなものではない。

ちなみに、ConvertTo-Xmlという似たようなコマンドレットがありますが、出力形式はCLIXMLではない上、復元の手段もなく、かといって別に読みやすいXMLというわけでもなく、正直何のために使うのかよく分かりません(適切なxsltでも用意すればいいのかな?)。まだConvertTo-Htmlの方が使えそうです。

psd1

psd1は「PowerShellデータファイル」で、モジュールマニフェストやローカライズデータに使われるファイル形式です。スクリプトファイルの1種ですが、数値や文字列リテラル、配列、連想配列、コメントなど基本的な言語要素のみ使用可能です。PowerShell 5.0以降ではImport-PowerShellDataFileコマンドレットを用いて、任意のpsd1ファイルのデータを読み込み、変数に格納することが可能です。

書き出し

書き出し用のコマンドレットはありません。

読み込み

例えば以下のような内容をbackup_setting.psd1として保存しておきます。ルート要素は必ず連想配列にします。

@{
	Directories = @(
		@{
			From = "C:\test1"       # コピー元
			To = "D:\backup\test1"  # コピー先
			Exclude = @("*.exe", "*.dll")
			Recurse = $true
		},
		@{
			From = "C:\test2"
			To = "D:\backup\test2"
			Exclude = @("*.exe")
			LimitSize = 50MB
		},
		@{
			From = "C:\test3"
			To = "D:\backup\test4"
		}
	)
	Start = "0:00"
}

なお、dataセクションで全体を括ってもいいですが、psd1で許容される言語要素はdataセクションより更に制限がきついので、敢えてしなくてもいいんじゃないかと思います。

このファイルは以下のように読み込めます。

$setting = Import-PowerShellDataFile -LiteralPath backup_setting.psd1

$settingには連想配列が格納され、以下のように値が参照できます。

$setting.Directories | foreach {Copy-Item -Path $_.From -Destination $_.To}
メリット
  • PowerShellの構文でデータを記述できる。
  • 通常のps1ファイルを呼び出すのとは異なり、式の評価やコマンド実行などはされない分、セキュアである。
  • 配列と連想配列の組み合わせにより、JSONライクな階層構造を持てる。型情報も保持される。
  • JSONとは違い、コメントが入れられる。
デメリット
まとめ

PowerShellで扱うデータをテキストファイルとして保存する際には、各テキストデータ形式の特性を理解し、メリット、デメリットを踏まえて選定する必要があります。

また、当然ながらテキストファイルに保持することが不適切なデータもありますので、そこは注意してください。(画像データを敢えてBase64とかでエンコードしてテキストファイル化する意味があるのか、とかですね)

個人的には…

ちょっとした作業ログ等を記録しておきたい→プレーンテキスト

.NETオブジェクトの一部のプロパティだけ抜き出してファイル化したい→CSV

自分で構築したPSCustomObjectをファイル化したい→JSON

.NETオブジェクト全体をファイル化したい→CLIXML

スクリプトで使う設定データを用意したい→psd1

みたいな感じでなんとなく使い分けていると思います。psd1はまだ採用例はないですが…。

今回はビルトインのコマンドレットで扱えるもののみ取り上げましたが、他にもyaml等のテキストデータ形式が存在し、有志によるモジュールを用いて扱うことが可能です。

2014/12/11

MMLとは

この記事はPowerShell Advent Calendar 2014の11日目の記事です。

突然ですが、MMLってご存知ですか? MMLとはMusic Macro Languageの略で、その名の通り音楽演奏データを記述するための言語です。BASICのPLAY文で使うあれです。MIDIファイルに変換するコンパイラもありましたよね。

たとえば、「ねこふんじゃった」の最初の部分をMMLで書くとこんな感じです。MMLはいろんな記法があるのですが、よくあるのはこういうのです。

e-d-<g-4>g-rg-4e-d-<g-4>g-rg-4e-d-<g-4>g-4<e-4>g-4<d-4>frf4
e-d-<d-4>frf4e-d-<d-4>frf4e-d-<d-4>f4<e-4>f4<g-4>g-rg-4

ドの音をc、レの音をd、のように以下シの音をbまでアルファベット音階で割り当てます。半音上げる(シャープ)場合は音名のあとに+、半音下げる(フラット)場合は-を付与します。音の長さは4分音符なら4、8分音符なら8と書きます。付点は数字のあとに.を付けます。数字省略時は8分音符と解釈するのが普通のようです。オクターブを上げる場合は>、下げる場合は<と書きます(コンパイラによっては逆の解釈をする)。休符はrです。

本物のMMLでは、音色の指定だとかトラックの割り当てだとか、もっとたくさん命令があるんですが、基本はだいたいこんなもんです。

PowerShellで音を鳴らすには

PowerShellで音を鳴らすには、.NETのSystem.Console.Beepメソッドを使うのが一番手っ取り早いです。ビープ音ですね。比較的古いWindowsではハードウェアのビープスピーカーから、比較的新しいWindowsではサウンドデバイス上で鳴ります。今回のMMLシーケンサーも結局はBeepメソッドです。

そしてぎたぱそ先生がずっと以前に書いておられます。なので今回のはまあ、MMLシーケンサー部分を除けば二番煎じなんですけどもね。(そしてSmallBasicLibraryを使えば実はMML演奏もできるので、車輪の再発明でもあるんですが)

ちなみにwavやmp3なんかを鳴らすには、Windows Media PlayerをCOM経由で実行する方法や.NET(WPF)のSystem.Windows.Media.MediaPlayerクラスを使う方法などがあります。

音程の決め方

Beepメソッドでは第1引数にビープ音の周波数をHzで、第2引数に再生時間をミリ秒で指定します。つまりは音程(音の高さ)はHzで指定する必要があります。ここでドの音は何ヘルツで、みたいな対応表をどこかから探してきてそのデータを使ってもいいんですが、今回はせっかくなので、PowerShellで計算して求めてみます。

まず、ある音程の一つ上のオクターブの音程の間の周波数比は、1:2です。

ピアノ等の鍵盤楽器が近くにあれば、それを見てもらえば分かると思いますが、1オクターブには12の音程が含まれています。1オクターブの周波数比を12個均等に分割します(この周波数の決め方を平均律といいます)。つまり、隣り合う音程の周波数比は、1:12√2となるわけです。

さて、音程の周波数比はこれでわかりました。あとは基準となる音の周波数が分かれば、全ての音の周波数が計算できるわけです。時代や地域、演奏する楽器等によって微妙に違ってきますが、現代日本においては普通は、A(ラ)=440Hzが基準周波数です。

あとは単純にかけ算して周波数を求めて連想配列に入れておくだけです。コードにすればこんな感じですね。

$baseFrequency = 440.0
$pitches = @{
            "c" = $baseFrequency * [math]::Pow(2, 3/12)
            "d" = $baseFrequency * [math]::Pow(2, 5/12)
...
}

あと、+で半音上がる場合は[math]::Pow(2, 1/12)をかけ、-で半音下がる場合は割ればいいだけです。(別に$pitchesテーブルで最初から定義してもいいかもですが)

音価の決め方

音価というのは音の長さです。Beepメソッドの第2引数にはミリ秒で実際の時間を指定する必要があるので、これも計算で求めてやります。

4分音符というのは、1小節(全音符)を4分割、つまり1/4した音価を持ちます。同様に8分音符は1/8ですね。付点がつくと音価は1.5倍になります。

ではたとえば4分音符の具体的な音価はどうやって定めるのでしょうか。それを決めるのがテンポと拍子です。

テンポは1分間(つまり60000ミリ秒)の拍数(BPM、Bit Per Minuteともいう)で定義されます。今回作るシーケンサーは4/4拍子、すなわち1拍=4分音符とする4拍子固定とするので、1分間に4分音符がBPM回含まれることになります。

つまり、4分音符の音価=60000ミリ秒/BPMの値、全音符の音価=60000*4ミリ秒/BPMの値 となるわけです。

具体的なコードは…略します。単なるかけ算と割り算だけなんで。

MMLのパース

えっと時間がなくなってきたので、駆け足で説明します。

MMLのパースとは、要するに最初の例のようなMML(テキストデータ)から、実際に演奏する音程と音価の組み合わせのデータとして組み立てる作業になります。

今回のシーケンサーでは、繰り返し記号等はサポートしないので、単にMMLを一文字ずつ読み込んで、音名(cなど)を見つけたら、その後の文字を読んで、+があれば半音上げるなどして周波数を求め、その後に数字があればn分音符とみなし、先ほどの計算式から実際の音価を求めてやる。というのを全部の音でくり返すだけです。

この処理をやっているのがConvertFrom-MMLという関数です。MMLテキストを入力してやると、周波数(Frequency)や発音時間(Duration)などをプロパティとして含んだpscustomobject(Noteオブジェクト)を出力します。(Noteとは音符のことです)

MMLの演奏

パースしたMMLを実際に演奏するのがInvoke-Beep関数です。入力は、ConvertFrom-MMLで生成したNoteオブジェクトです。実際の処理は至って単純で、 [System.Console]::Beep($note.Frequency, $note.Duration)するだけです(えー)。あ、あと休符の時はDuration分だけSleepを入れてやっています。

MMLの>と<の意味を逆転する-Reverseスイッチ、BPMを指定する-Bpmパラメータ(デフォルト120)なんかも用意してます。

実際にはMMLから直接演奏できるように、Invoke-Mmlというラッパー関数を用意しています。

冒頭のねこふんじゃったのMMLを再生するにはこんな感じにします。

$s=@"
e-d-<g-4>g-rg-4e-d-<g-4>g-rg-4e-d-<g-4>g-4<e-4>g-4<d-4>frf4
e-d-<d-4>frf4e-d-<d-4>frf4e-d-<d-4>f4<e-4>f4<g-4>g-rg-4
"@
Invoke-Mml $s
コード
function ConvertFrom-MML
{
    [CmdletBinding()]
    param(
        [parameter(ValueFromPipeline=$true)][string[]]$mml,
        [int]$bpm = 120,
        [switch]$reverse = $false
    )
    begin
    {
        $baseFrequency = 440.0
        $scaleRatio = [math]::Pow(2, 1/12)
        $baseDuration = 60000 * 4 / $bpm
        $pitches = @{
            "c" = $baseFrequency * [math]::Pow(2, 3/12)
            "d" = $baseFrequency * [math]::Pow(2, 5/12)
            "e" = $baseFrequency * [math]::Pow(2, 7/12)
            "f" = $baseFrequency * [math]::Pow(2, 8/12)
            "g" = $baseFrequency * [math]::Pow(2, 10/12)
            "a" = $baseFrequency * [math]::Pow(2, 12/12)
            "b" = $baseFrequency * [math]::Pow(2, 14/12)
            "r" = -1
        }
        $currentOctave = 0
    }

    process
    {
        $chars = [string[]]$mml.ToCharArray()

        0..($chars.Length - 1)|%{
            if($null -ne $pitches[$chars[$_]])
            {
                $frequency = $pitches[$chars[$_]]
                $suffix = ""
            
                $denominator = ""
                $dot = $false

                if($_ -lt $chars.Length-1)
                {
                    switch($chars[($_ + 1)..($chars.Length - 1)])
                    {
                        {$pitches.Contains($_)}
                            {break}
                        "+"
                            {$frequency *= $scaleRatio; $suffix += $_}
                        "-"
                            {$frequency /= $scaleRatio; $suffix += $_}
                        {$_ -match "\d"}
                            {$denominator += $_}
                        "."
                            {$dot = $true}
                    }
                }

                $frequency *= [math]::Pow(2, $currentOctave)

                $denominator = [int]$denominator
                if($denominator -eq 0){$denominator = 8}
                $multiplier = 1 / $denominator
                
                if($dot){$multiplier *= 1.5}
                $duration = $baseDuration * $multiplier

                if($frequency -le 0){$on = $false}else{$on = $true}
                [pscustomobject]@{
                    Frequency = [int]$frequency
                    Duration = $duration
                    Note = "$($chars[$_])$suffix$denominator$(if($dot){"."})"
                    Octave = $currentOctave
                    Pitch = $chars[$_]
                    Suffix = $suffix
                    Denominator = "$denominator$(if($dot){"."})"
                    On = $on
                }
            }
            elseif($chars[$_] -eq ">")
            {
                if($reverse)
                {
                    $currentOctave--
                }
                else
                {
                    $currentOctave++
                }
            }
            elseif($chars[$_] -eq "<")
            {
                if($reverse)
                {
                    $currentOctave++
                }
                else
                {
                    $currentOctave--
                }
            }
        }
    }
}

function Invoke-Beep
{
    [CmdletBinding()]
    param([parameter(ValueFromPipeline = $true)][psobject[]]$note)
    
    process
    {
        if($note.On)
        {
            [System.Console]::Beep($note.Frequency, $note.Duration)
        }
        else
        {
            Start-Sleep -Milliseconds $note.Duration
        }
    }
}

function Invoke-Mml
{
    [CmdletBinding()]
    param(
        [string]$mml,
        [int]$bpm,
        [switch]$reverse
    )
    
    ConvertFrom-Mml @PSBoundParameters|Invoke-Beep
}
おわりに

今回はMMLをパースしてBeepを演奏するスクリプトをPowerShellで作ってみました。これをシーケンサーというのはおこがましいと思いますが、まあたまにはこういう柔らかいネタもいいんじゃないでしょうか。ドレミの音がどうやって決められてるのかというのも、もしかして話のネタくらいにはなるかも。

そしてこれ、シーケンサーといいつつ、入力機能がないですよね。それについては次回やります。

PowerShellアドベントカレンダー2014はまだまだ残席ございます。ぜひ、あなたのPowerShell話を聞かせて下さい。

2014/09/09

8/23わんくま横浜勉強会で、PowerShellコマンドの書き方というセッションをしたのですが、その際、株式会社Codeerさんが公開されているFriendlyというライブラリを使ったコマンドレットを動作させるデモを行いました。

準備の時間がなくて、突貫工事で作ったサンプルで恐縮ですが、公開することにします。(一応動かしてみたら動いた、レベルのものなのであしからず…)

コード
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Dynamic;
using System.Windows.Forms;

namespace Winscript
{
    [Cmdlet(VerbsCommon.Get, "FormControlText")]
    public class GetFormControlTextCommand : Cmdlet
    {
        [Parameter(Mandatory = false, ValueFromPipeline = false, Position = 1)]
        public string[] ControlName { get; set; }

        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
        public Process Process { get; set; }

        protected override void ProcessRecord()
        {
            var _app = new WindowsAppFriend(this.Process);
            dynamic form = _app.Type<Control>().FromHandle(this.Process.MainWindowHandle);
            foreach (var c in form.Controls)
            {
                if (ControlName == null || ControlName.Contains((string)c.Name))
                {
                    WriteObject((string)c.Text);
                }
            }
        }
    }

    [Cmdlet(VerbsCommon.Set, "FormControlText")]
    public class SetFormControlTextCommand : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = false, Position = 1)]
        public string ControlName { get; set; }

        [Parameter(Mandatory = true, ValueFromPipeline = false, Position = 2)]
        public string Text { get; set; }

        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
        public Process Process { get; set; }

        protected override void ProcessRecord()
        {
            var _app = new WindowsAppFriend(this.Process);
            dynamic form = _app.Type<Control>().FromHandle(this.Process.MainWindowHandle);
            foreach (var c in form.Controls)
            {
                if (ControlName == (string)c.Name)
                {
                    c.Text = Text;
                }
            }
        }
    }
}
ビルド方法
  1. Windows 8.1 SDK をインストールする。
  2. Visual Studio 2010以降でC#のクラスライブラリを新規作成する。
  3. C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0 にある.dllを参照設定する。
  4. 対象フレームワークを.NET 4.5、対象プラットフォームをx64にする。
  5. 上記のコードを貼り付ける。
  6. NuGetでFriendlyおよびFriendly.Windowsを追加する。
  7. ビルドする。
使用方法

上記をビルドして生成したDLLにはGet-FormControlTextと、Set-FormControlTextの2つのコマンドレットが含まれます。

# コマンドレットのインポート
Import-Module "dllのフルパス"

# 操作対象のプロセスオブジェクト取得
$p = Get-Process WindowsFormsApplication1 

# プロセスを指定して、すべてのコントロールのテキストを取得
Get-FormControlText -Process $p

# プロセスとコントロール名を指定して、テキストを取得
Get-FormControlText -Process $p -ControlName textBox1

# 位置パラメータなので以下のようにも書ける
Get-FormControlText $p textBox1

# パイプラインからプロセスオブジェクトを入力することもできる
$p | Get-FormControlText -ControlName textBox1 

# プロセスとコントロール名を指定して、テキストを変更する
Set-FormControlText -Process $p -ControlName textBox1 -Text Wankuma
Set-FormControlText $p textBox1 Yokohama
$p | Set-FormControlText -ControlName textBox1 -Text 6
制限事項

フォーム直下に配置されたコントロールしか取得できない(と思います)。

Windows Formアプリケーションにしか対応していない(と思います)。

x64アプリしか操作できない(と思います。x86用はアセンブリを分ける必要がある??)。(←9/9追記)

コントロール名指定はCase sensitiveでワイルドカード不可です。(この辺ただの手抜きですが)

今後の方針?

上のコードをみてもらえればわかると思いますが、ちょっと触ってみたら一応動くものができるくらい、Friendlyは分かりやすいです。皆さんもぜひ使ってみてください。

本来は、テキストじゃなくてコントロールそのものをGetしたりSetしたりInvoke(クリックとか)したりできるようにしたかったんですが、Controlはシリアル化できないオブジェクトなので、Friendlyで実物を持ってきてコマンドレットに出力する、というのは無理でした。何らかのプロキシオブジェクトみたいなのでラップしたりすれば良いかと思います。

それにしてもPowerShellとFriendlyの組み合わせはものすごい可能性を秘めている予感がします。

システム管理方面では…

セッションでもやりましたが、PowerShellコマンドレットベースのアプリケーションを作ると、GUIとCUIのいいところどりが出来る、とはいうものの、コマンドインターフェースがなくGUIオンリーのアプリケーションはまだまだたくさんあるのが現実かと思います。そういうアプリケーションも、PowerShellデフォルトの機能のみではつらいですが、上記のようにFriendlyを併用すれば、GUI操作の自動化が容易になると思います。

開発方面では…

C#でFriendlyを使ったGUIのテストコードを書く以外に、上記のようなFriendlyの機能をラップしたコマンドレットを用意することで、PowerShellスクリプトでもテストコードを書くことができるようになると思います。その際、Pester等のPowerShell用テストフレームワークを併用すると、より一貫したテストコードが記述できるようになるんじゃないかと思います。

いずれは(いつ?)、Friendlyの機能をラップしたコマンドレット群をきちんと設計、実装して、モジュールとして公開したいですね!

2007/11/05

体調が悪くてブログ更新とかWeb連載記事が止まっていてすみません。ほかの仕事もあまりできてませんし、オフラインイベントにも出てないですねー。

左半身(手足、顔など)がしびれたり震えたりしておかしいんです。あと胃腸の調子が悪くて…。

12/8は名古屋で、1月には大阪でスピーカーをやらせていただくので、それまでには治しておきたいところです。とりあえず検査中なのです。勉強会へのご参加お待ちしてます。

12/8と1月の講演はどちらも同じ内容でPowerShellがお題ですが、基礎編はちゃちゃっと流して少し「コマンドレットの組み合わせ」をテーマに応用に入ります。演題は「Windows PowerShell ステップアップ講座」にします。>中さんよろしくです

基礎編は大阪#5大阪#4などで復習しておいていただけると嬉しいです。

1月大阪は二コマいただいてまして、もう一コマで、「初音ミク」をテーマに少ししゃべろうかなと思ってます。初音ミクと作曲のイロハのイをやろうと思います。現在、検証中&調整中です。初音ミクは購入したんですが体験版が入手できなくてデモができるかどうか微妙なんですよ。でもデモできないと面白くない(というか意味があまりない)のでなんとか調整しますね。

作曲は自己流なので、講演もかなり偏ると思いますが、DTM(デスクトップミュージック)のここ10年の歴史をMIDIという単語をキーワードに少し語ったあと、素人でも、しかもお金をかけずともフィーリングで作曲できるということを示したいと思っています。そのあと、自作曲に乗せてミクさんに歌ってもらうという流れ。あ、どうしましょ、デモ用の曲を作らないと(汗

実は、すでにこんな曲を作ったりしていますがw
やまにょん氏と、謎の歌手largeさんsmallさんと組んで作った「まい☆びす」と「Happy! Lucky! Scripting!!」という電波ソングです。二つとも私が作詞、前者はやまにょん氏作曲、後者は私作曲です。

さて話はぜんぜんかわりますが、FizzBuzz問題(えぴさんとこにリンク)が解けて安心した人には、FizzBuzzDozz問題を解いてもらいます。私が今考えました。

【問題】
1から100までの数を表示するプログラムを書け。
ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、
3と5両方の倍数の場合には「Dozz」とプリントすること。

FizzBuzz問題は実は数学を知らなくてもできますが、この問題を美しく解くには数学の知識が必要かもしれません。個人的には、FizzBuzzで素質を調べ、FizzBuzzDozzで発展性を調べるという感じですね。これができないと将来的にはつらくなってくると思います。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/11/05/105917.aspx

2007/06/06

PowerShellは@や$をよく使いますが、カッコとの組み合わせでたまに意味不明になってくるので、まとめてみました。たぶん組み合わせを網羅してると思います。

$a
# 普通の変数宣言。
 
$(1)
# 1(System.Int32)と等しい。中の式が返す値を出力する。
@(1) # 1要素だけ含まれた長さ1の配列(System.Object[])(,1でもよい)



@(1,2,3) # 普通の配列。(単に1,2,3でもよい)
$() # null
@() # 空の(長さ0の)配列(System.Object[])
@{} # 空の連想配列(System.Collections.Hashtable)
@{a=1;b=2} # 普通の連想配列。
${hello world} # 空白などを含む場合の変数宣言
${C:file.txt} # カレントにあるファイルの中身を取得
元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/06/06/79806.aspx

Copyright © 2005-2016 Daisuke Mutaguchi All rights reserved

mailto: mutaguchi at roy.hi-ho.ne.jp

Awards

Books

Twitter