PowerShell バージョンごとのスクリプティング機能の新機能概略


PowerShell v3 以降のそれぞれのバージョン毎のスクリプティングを中心とした新機能のうち、私の独断で選んだ主な機能を列挙してみた。
(PowerShell の本来の役割のはずの) コンピュータ管理機能などはばっさり省いている。

意外と、そういう情報まとまっているのを見たことなかったので。。。

v3 から

PSCustomObject の型変換

ハッシュテーブル を、 Key-Value ペアを ノートプロパティ に変換した カスタムオブジェクト に型変換できる。
参考: about_Hash_Tables

# On v2
$t = [PSCustomObject]@{Hoge=1; Fuga=3;};
$t.GetType();
# -> Hashtable
$t.PSObject.Properties | ft
# -> 
#    Name
#    ----
#    IsReadOnly
#    IsFixedSize
#    IsSynchronized
#    Keys
#    Values
#    SyncRoot
#    Count
# On v3
#Requires -Version 3
$t = [PSCustomObject]@{Hoge=1; Fuga=3;};
$t.GetType();
# -> PSCustomObject
$t.PSObject.Properties | ft
# -> 
#    Name
#    ----
#    Hoge
#    Fuga

ordered

ハッシュテーブルリテラル @{}[ordered] 属性ををつけることで、 ハッシュテーブル の代わりに 順序付けされたディクショナリ ([System.Collections.Specialized.OrderedDictionary]) を作成できる。
また、 この順序付けされたディクショナリを [PSCustomObject] に型変換すると、プロパティ順が維持された状態の カスタムオブジェクト が作成される。
参考: about_Hash_Tables

#Requires -Version 3
$o1 = @{Hoge=0; Fuga=100; Hogera=10000; Piyo=1MB}
$o2 = [ordered]@{Hoge=0; Fuga=100; Hogera=10000; Piyo=1MB}
$o1.GetType()
# -> Hashtable
$o2.GetType()
# -> OrderedDictionary
[PSCustomObject]$o1
# -> 
#    Hogera    Piyo Fuga Hoge
#    ------    ---- ---- ----
#     10000 1048576  100    0
[PSCustomObject]$o2
# -> 
#    Hoge Fuga Hogera    Piyo
#    ---- ---- ------    ----
#       0  100  10000 1048576

ハッシュテーブルからの任意のオブジェクトの作成

パラメーターを持たないコンストラクター があるクラスに対してのみ、プロパティとプロパティ値のハッシュテーブルからクラスのオブジェクトを作成できる。
オブジェクトのプロパティは、パブリックかつ設定可能である必要がある。
参考: about_Object_Creation

#Requires -Version 3
$p = [System.Diagnostics.Process]::Start(
    [System.Diagnostics.ProcessStartInfo]@{
        FileName = 'cmd.exe';
        Arguments = '/s /c "echo foobar&pause"';
    }
);

必要なシチュエーションは限定的と思われるが、 プロパティの設定順が重要な場合は [ordered] を併用する。
また、 ハッシュテーブルが CaseSensitive であるかどうかにかかわらず、 プロパティ名は IgnoreCase で評価され、 複数のキーがプロパティ名と一致する場合は 複数回プロパティに代入される。

#Requires -Version 3
Add-Type -Language CSharpVersion3 -TypeDefinition @'
public class TestClass {
    int _c = 0;
    string _pA; private string _pB; private string _pC;
    public string PropA { get { return _pA; } set { _pA = value + (_c++).ToString(); } }
    public string PropB { get { return _pB; } set { _pB = value + (_c++).ToString(); } }
    public string PropC { get { return _pC; } set { _pC = value + (_c++).ToString(); } }
}
'@;

# 各プロパティに格納される順番は順不同
[TestClass]@{PropA='Hoge'; PropB='Fuga'; PropC='Piyo'};
# -> 
#    PropA PropB PropC
#    ----- ----- -----
#    Hoge2 Fuga1 Piyo0

# 各プロパティに格納される順番は、ハッシュリテラルに登場した順
[TestClass][ordered]@{PropA='Hoge'; PropB='Fuga'; PropC='Piyo'};
# -> 
#    PropA PropB PropC
#    ----- ----- -----
#    Hoge0 Fuga1 Piyo2

# 大文字小文字は無視され、 PropA, PropB, PropC, PropA (上書き) の順番で、各プロパティに設定される
$chash = New-Object System.Collections.Specialized.OrderedDictionary; # CaseSensitive
$chash.PropA='Hoge';
$chash.PropB='Fuga';
$chash.PropC='Piyo';
$chash.propa='FooBar';
[TestClass]$chash;
# -> 
#    PropA   PropB PropC
#    -----   ----- -----
#    FooBar3 Fuga1 Piyo2

ちなみに、 大文字小文字だけが異なる名前のフィールドやプロパティを含むクラスのオブジェクトは、 そもそも PowerShell で取り扱うことが出来ない。

Add-Type -Language CSharpVersion3 -TypeDefinition @'
public class TestClass2 {
    public string PropA { get; set; }
    public string PropB { get; set; }
    public string PropC { get; set; }
    public string propa { get; set; }
}
'@;
New-Object TestClass2;
# -> error: The field or property: "propa" for type: "TestClass2" differs only in letter casing from the field or property: "PropA". The type must be Common Language Specification (CLS) compliant.

Simplified Syntax

# On v2
Get-Process | ? { $_.PM -gt 10MB } | %{ $_.Name }
# On v3
#Requires -Version 3
Get-Process | ? PM -GT 10MB | % Name

ネイティブコマンドエスケープ

#Requires -Version 3
cmd.exe /c echo $home
# -> C:\Users\****
#    $home 変数が展開される
cmd.exe --% /c echo $home
# -> $home
#    $home 変数が展開されない

Member Enumeration

$array = @("Hoge", "Fuga");
$array.ToUpper();
# On v2 -> 
#    error
# On v3 -> 
#    HOGE
#    FUGA

$single1, $single2 = "Hoge", 9;
$single1.Length;
$single2.Length;
# On v2 -> 
#    4     # String.Length
#    $null
# On v3 -> 
#    4     # String.Length
#    1     # Arrey でも Enumerable でもなく Length プロパティを持たないものに Length プロパティをつけると、 1 が返される
$single1.Count;
$single2.Count;
# On v2 -> 
#    $null # String に Count プロパティは存在しない
#    $null
# On v3 -> 
#    1     # Count プロパティを持たないものに Count プロパティをつけると、 1 が返される
#    1

$single1[0];
$single2[0];
# On v2 -> 
#    H
#    error: System.Int32 のオブジェクトにインデックスを付けることはできません。
# On v3 -> 
#    H     # String 型のインデクサが機能している
#    9     # インデックスを持たないものに [0] 又は [-1] のインデックスをつけると、 オブジェクトそのものが返される

例えば、 Get-ChildItem などのコマンドレットは、 オブジェクトを 1つ だけ返すか 2つ以上返すかわからず、結果を 変数に入れた際の型が [Object[]] となるか [System.IO.FileSystemInfo] となるかわからない。
しかし、この機能を使うと どちらであっても同じように扱うことができる。

#Requires -Version 3
$x = Get-ChildItem; # returns 1 or more objects
for ($i = 0; $i -lt $x.Count; $i++) {
    $x[$i];
}

詳しくは、 about_Arrays ヘルプトピックを参照して欲しいところだが、 『0 個または 1 個のオブジェクトを含むコレクションで、Count プロパティと Length プロパティを使用できます。また、1 個のオブジェクトを含む配列にインデックスを付けることもできます。』 と書かれていて説明としては微妙。
https://technet.microsoft.com/ja-jp/library/hh847882.aspx

ただし、 [PSCustomObject] では この Count プロパティと Length プロパティ が使用できないという、意図のよくわからない制限がある。

Set-StrictMode -Version 3.0 による 配列の境界外へのアクセスチェック

#Requires -Version 3
Set-StrictMode -Version 2.0;
@(1,2,3)[3];
# -> $null
Set-StrictMode -Version 3.0;
@(1,2,3)[3];
# -> error: Index was outside the bounds of the array.

ハッシュテーブル を デシリアライズ した場合の挙動の変更

v2 では ハッシュテーブル をデシリアライズした場合に常に 大文字と小文字を区別する デフォルトの ハッシュテーブル となるが、 v3 では キーに重複がない場合は 大文字と小文字を区別しない ハッシュテーブル となる。

$a1 = @{};                      # Key は CurrentCultureIgnoreCase される
$b1 = New-Object hashtable;     # Key は CaseSensitive される
$a1['a'] = 1;
$a1['A'] = 2;
$b1['a'] = 1;
$b1['A'] = 2;

$a1;
# -> 
#    Name Value
#    ---- -----
#    a    2
[hashtable].GetProperty('EqualityComparer', [System.Reflection.BindingFlags]'Instance, NonPublic').GetValue($a1, $null);
# -> System.CultureAwareComparer

$b1;
# -> 
#    Name Value
#    ---- -----
#    A    2
#    a    1
[hashtable].GetProperty('EqualityComparer', [System.Reflection.BindingFlags]'Instance, NonPublic').GetValue($b1, $null);
# -> $null


# 一旦シリアライズしてからデシリアライズする
$a1 | Export-Clixml "$env:TEMP\t.xml";
$a2 = Import-Clixml "$env:TEMP\t.xml";
$b1 | Export-Clixml "$env:TEMP\t.xml";
$b2 = Import-Clixml "$env:TEMP\t.xml";
$a2['b'] = 3;
$a2['B'] = 4;
$b2['b'] = 3;
$b2['B'] = 4;

$a2; # v2 では常に CaseSensitive となるのに対して、 v3 では デシリアライズしたキーに重複がなければ CurrentCultureIgnoreCase となる。
# On v2 -> 
#    Name Value
#    ---- -----
#    b    3
#    B    4
#    a    2
#
# On v3 -> 
#    Name Value
#    ---- -----
#    a    2
#    b    4
[hashtable].GetProperty('EqualityComparer', [System.Reflection.BindingFlags]'Instance, NonPublic').GetValue($a2, $null);
# On v2 -> 
#    $null
# On v3 -> 
#    System.CultureAwareComparer

$b2; # デシリアライズしたキーに重複があれば、どちらも CaseSensitive となる。
# -> 
#    Name Value
#    ---- -----
#    b    3
#    B    4
#    a    1
#    A    2
[hashtable].GetProperty('EqualityComparer', [System.Reflection.BindingFlags]'Instance, NonPublic').GetValue($b2, $null);
# -> $null

演算子

  • 算術演算子
    • ビットシフト -shr (右シフト) と -shl (左シフト) の追加
  • 比較演算子
    • -in (-iin, -cin), -notin (-inotin, -cnotin) 演算子

コマンドレット

  • Invoke-RestMethod
  • Invoke-WebRequest # Invoke-WebRequest のエイリアスが curl, wget に設定されている。 このため、 curl を呼び出す際などは、 curl.exe と書かないといけない。
  • ConvertFrom-Json
  • ConvertTo-Json

など

その他

  • PS Workflow
    • Windows Workflow Foundation 的な何か
  • バックグラウンドジョブ

など

v4

Dynamic Method

インスタンスメソッド:

$object = $PWD;
$prop = "Path";
$method = "GetType";
$object.$prop;
# -> C:\Users\****
#    # プロパティについては、 v2 の頃から使えていた

$object.$method();
# On v3 -> 
#    error
# On v4 -> 
#    IsPublic IsSerial Name     BaseType
#    -------- -------- ----     --------
#    True     False    PathInfo System.Object

# 以下の様な記述であれば、 v2 の頃から使えていた
$object.$method.Invoke()
# -> 
#    IsPublic IsSerial Name     BaseType
#    -------- -------- ----     --------
#    True     False    PathInfo System.Object

スタティックメソッド:

$type = [System.IO.Path];
$prop = "DirectorySeparatorChar";
$method = "ChangeExtension";
$type::$prop;
# -> \
#    # プロパティについては、 v2 の頃から使えていた

$type::$method('hoge.xml', 'json');
# On v3 -> 
#    error
# On v4 -> 
#    hoge.json

# 以下の様な記述であれば、 v2 の頃から使えていた
$type::$method.Invoke('hoge.xml', 'json');
# -> hoge.json

メソッド構文でのフィルタリング

パイプするより早いらしい

#Requires -Version 4
$data = 1..1MB
$data.Where({$_ % 250000 -eq 0}).ForEach({ "out: $_" });
# -> 4秒
$data | ?{$_ % 250000 -eq 0} | %{ "out: $_" };
# -> 19秒

#Requires での管理者権限必須化

#Requires -RunAsAdministrator

コマンドレット

  • Get-FileHash

など

その他

  • DSC
    • Configuration as Code 的な何か

など

v5

クラス

#Requires -Version 5
class MyClass { [int]Piyo([int]$a) { return $a * $this.Fuga * $this.Hoge } $Fuga = 2; Hidden $Hoge; MyClass() { $this.Hoge = 3 } }
$o = New-Object MyClass; # 又は、 [MyClass]::new()
$o.Piyo(5);
# -> 30
$o
# -> 
#    Fuga
#    ----
#       2

メソッドで値を帰す場合は 戻り値型の指定や return 文が必須だったりと、 PowerShell の関数の感覚からするといろいろ変で、 C# のクラスの機能に近い。

using namespace

#Requires -Version 5
using namespace System.Collections.Generic;
New-Object List[int];

Auto-Generated Example-Driven Parsing

Excel 2013 の フラッシュフィル のような、 変更例の法則をみて いい感じに残りの変換をやってくれる機能

#Requires -Version 5
"Lee Holmes", "Steve Lee", "Jeffrey Snover", "Steve Jobs", "Yukihiro Matsumoto" | Convert-String -Example "Bill Gates=Gates, B.", "John Smith=Smith, J."
# ->
#    Holmes, L.
#    Lee, S.
#    Snover, J.
#    Jobs, S.
#    Matsumoto, Y.

残念ながら、 現在のところ v6 α には含まれてい模様。

コマンドレット

  • Set-Clipboard / Get-Clipboard
    • Get-Clipboard -Format FileDropList とすると、クリップボード内のファイルの FileInfo が取得できたり、なかなか便利。
  • Compress-Archive / Expand-Archive
  • Format-Hex
  • New-Guid

など

その他

  • DSC 強化
  • デバッグ機能の強化
  • PackageManagement
    • chocolatey や NuGet や PowerShellGet や 「プログラムと機能」 を透過的に扱うためのもの
  • 管理機能いっぱい

など

v5.1 から

コア エディション と デスクトップエディション

https://msdn.microsoft.com/ja-jp/powershell/wmf/5.1/scenarios-features

  • デスクトップ エディション
    • .NET Framework 上に構築されており、Server Core や Windows Desktop などの Windows の完全エディションで実行する PowerShell のバージョンを対象とするスクリプトおよびモジュールとの互換性を提供します。
  • コア エディション
    • .NET Core 上に構築されており、Nano Server や Windows IoT などの Windows の縮小エディションで実行する PowerShell のバージョンを対象とするスクリプトおよびモジュールとの互換性を提供します。

その他

  • DSC 強化
  • モジュールバージョンの指定
  • パイプライン速度の向上

v6 (アルファリリース)

OSS & クロスプラットフォーム

Windows, Linux, OS X のクロスプラットフォーム でリリースされている。
https://github.com/PowerShell/PowerShell

.NET Core 上に構築されているため、 使用できる .NET の機能も .NET Core のものになるので注意。


とりあえずこんなところだが、随時追記していきたい。

更新履歴

  • 2016-09-01
    • 追加
      • ハッシュテーブルからの任意のオブジェクトの作成
      • ハッシュテーブル を デシリアライズ した場合の挙動の変更
    • 追記
      • Dynamic Method

コメントを残す

メールアドレスが公開されることはありません。