Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9571

C#の記事を参考にしてPowershellで何かしたいときに読むメモ(スターターキット)

$
0
0

Powershellで.NETを扱う

C#の記事を参考にしてPowershellで.Netを扱いたい場合の覚え書きです。

環境

PS/workspaces>$PSVersionTable.PSVersionMajorMinorPatchPreReleaseLabelBuildLabel----------------------------------------702

実行エンジン… .NET Core 3.1.5
参考:v7.0.2 Release of Powershell

入門

その前に:インテリセンス(補完)機能

Powershellのコマンド名はとにかく長い。そして、.NETのクラスの名前空間なども長い。
ただし、インテリセンス(補完)機能は割と強力。

IntelliSense1_command.gif

名前空間が分からないクラスを探すときなども便利。

IntelliSense2_class.gif

もちろんコマンドレットでもインテリセンスを使える。

IntelliSense3_commandlet.gif

Windowsの場合はCtrl+Spacebarで最初から使用可能。
Linuxの場合はデフォルトでは設定されてないので$profile等に設定コマンドを記述して使えるようにする。

# 基本的にコマンドが割り当てられてないキーなら何処でも良い。Set-PSReadLineKeyHandler-ChordAlt+q-FunctionMenuComplete

※この記事はPowershellの補完機能を前提にしています。

Get-Member

数値

PS/workspaces>17|gm# Get-Memberのエイリアス

実行結果
TypeName:System.Int32NameMemberTypeDefinition------------------------CompareToMethodintCompareTo(System.Objectvalue),intCompareTo(intvalue),intIComparable.CompareTo(System.Objectobj),intIComparable[int].CompareTo(intother)EqualsMethodboolEquals(System.Objectobj),boolEquals(intobj),boolIEquatable[int].Equals(intother)GetHashCodeMethodintGetHashCode()GetTypeMethodtypeGetType()GetTypeCodeMethodSystem.TypeCodeGetTypeCode(),System.TypeCodeIConvertible.GetTypeCode()ToBooleanMethodboolIConvertible.ToBoolean(System.IFormatProviderprovider)ToByteMethodbyteIConvertible.ToByte(System.IFormatProviderprovider)ToCharMethodcharIConvertible.ToChar(System.IFormatProviderprovider)ToDateTimeMethoddatetimeIConvertible.ToDateTime(System.IFormatProviderprovider)ToDecimalMethoddecimalIConvertible.ToDecimal(System.IFormatProviderprovider)ToDoubleMethoddoubleIConvertible.ToDouble(System.IFormatProviderprovider)ToInt16MethodshortIConvertible.ToInt16(System.IFormatProviderprovider)ToInt32MethodintIConvertible.ToInt32(System.IFormatProviderprovider)ToInt64MethodlongIConvertible.ToInt64(System.IFormatProviderprovider)ToSByteMethodsbyteIConvertible.ToSByte(System.IFormatProviderprovider)ToSingleMethodfloatIConvertible.ToSingle(System.IFormatProviderprovider)ToStringMethodstringToString(),stringToString(stringformat),stringToString(System.IFormatProviderprovider),stringToString(stringformat,System.IFormatProviderprovider),striToTypeMethodSystem.ObjectIConvertible.ToType(typeconversionType,System.IFormatProviderprovider)ToUInt16MethodushortIConvertible.ToUInt16(System.IFormatProviderprovider)ToUInt32MethoduintIConvertible.ToUInt32(System.IFormatProviderprovider)ToUInt64MethodulongIConvertible.ToUInt64(System.IFormatProviderprovider)TryFormatMethodboolTryFormat(System.Span[char]destination,[ref]intcharsWritten,System.ReadOnlySpan[char]format,System.IFormatProviderprovider)

文字列

PS/workspaces>'Foo'|gm

実行結果
TypeName:System.Int32NameMemberTypeDefinition------------------------CompareToMethodintCompareTo(System.Objectvalue),intCompareTo(intvalue),intIComparable.CompareTo(System.Objectobj),intIComparable[int].CompareTo(intother)EqualsMethodboolEquals(System.Objectobj),boolEquals(intobj),boolIEquatable[int].Equals(intother)GetHashCodeMethodintGetHashCode()GetTypeMethodtypeGetType()GetTypeCodeMethodSystem.TypeCodeGetTypeCode(),System.TypeCodeIConvertible.GetTypeCode()ToBooleanMethodboolIConvertible.ToBoolean(System.IFormatProviderprovider)ToByteMethodbyteIConvertible.ToByte(System.IFormatProviderprovider)ToCharMethodcharIConvertible.ToChar(System.IFormatProviderprovider)ToDateTimeMethoddatetimeIConvertible.ToDateTime(System.IFormatProviderprovider)ToDecimalMethoddecimalIConvertible.ToDecimal(System.IFormatProviderprovider)ToDoubleMethoddoubleIConvertible.ToDouble(System.IFormatProviderprovider)ToInt16MethodshortIConvertible.ToInt16(System.IFormatProviderprovider)ToInt32MethodintIConvertible.ToInt32(System.IFormatProviderprovider)ToInt64MethodlongIConvertible.ToInt64(System.IFormatProviderprovider)ToSByteMethodsbyteIConvertible.ToSByte(System.IFormatProviderprovider)ToSingleMethodfloatIConvertible.ToSingle(System.IFormatProviderprovider)ToStringMethodstringToString(),stringToString(stringformat),stringToString(System.IFormatProviderprovider),stringToString(stringformat,System.IFormatProviderprovider),striToTypeMethodSystem.ObjectIConvertible.ToType(typeconversionType,System.IFormatProviderprovider)ToUInt16MethodushortIConvertible.ToUInt16(System.IFormatProviderprovider)ToUInt32MethoduintIConvertible.ToUInt32(System.IFormatProviderprovider)ToUInt64MethodulongIConvertible.ToUInt64(System.IFormatProviderprovider)TryFormatMethodboolTryFormat(System.Span[char]destination,[ref]intcharsWritten,System.ReadOnlySpan[char]format,System.IFormatProviderprovider)

メソッド実行

PS/workspaces>'Foo'.ToUpper()FOO

キャスト

幾つか方法がある。

PS/workspaces>'Foo'-as[char[]]FooPS/workspaces>[char[]]'Foo'Foo

多段キャスト

この場合はas演算子を使った方がわかりやすい気がする。

PS/workspaces>[int[]][char[]]'Foo'70111111PS/workspaces>'Foo'-as[char[]]-as[int[]]70111111

組み合わせる

Foreach()はコレクションで使用可能なPowershell固有の特殊メソッド。
他にはClear()Where()がある。
Powershell7では差が縮まったがForeach-Objectよりもパフォーマンスに優れる。

配列について知りたかったことのすべて
Methods of arrays

PS/workspaces>'Foo'-as[char[]]-as[Byte[]]|ForEach-Object{$_+10-as[char]}|Join-StringPyyPS/workspaces>('Foo'-as[char[]]-as[Byte[]]).ForEach{$_+10-as[char]}-join''Pyy

オブジェクトを生成する方法

Powershellでオブジェクトを扱う方法は幾つもある。
公式の解説はこちら。
About Object Creation

この記事で扱うのは以下の通り。

  1. 静的メソッドnew()でクラスのコンストラクタを実行する
  2. 連想配列からキャストする
  3. New-Objectを使う

この記事では詳しく扱わないがSystem.ActivatorCreateInstance()を使う方法もある。

PS>$list=[System.Activator]::CreateInstance([System.Collections.Generic.List[int]])PS>$list.Count0PS>$list.AddRange([int[]]@(1..10))PS>$list[4..7]5678PS>$list=[System.Activator]::CreateInstance([System.Collections.Generic.List[int]],[int[]]@(1..5))PS>$list12345

静的メソッドnew()でコンストラクタを呼び出す

Powershell5以降、クラスのコンストラクタは静的メソッドnew()で呼び出せる。
C#のnewに相当する。コンストラクタ有無はGetConstructors().Countで確認可能。

PS>[string].GetConstructors().Count9PS>[string]::newOverloadDefinitions-------------------stringnew(char[]value)stringnew(char[]value,intstartIndex,intlength)stringnew(System.Char*,System.Private.CoreLib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=7cec85d7bea7798evalue)stringnew(System.Char*,System.Private.CoreLib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=7cec85d7bea7798evalue,intstartIndex,intlength)stringnew(System.SByte*,System.Private.CoreLib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=7cec85d7bea7798evalue)stringnew(System.SByte*,System.Private.CoreLib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=7cec85d7bea7798evalue,intstartIndex,intlength)stringnew(System.SByte*,System.Private.CoreLib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=7cec85d7bea7798evalue,intstartIndex,intlength,System.Text.Encodingenc)stringnew(charc,intcount)stringnew(System.ReadOnlySpan[char]value)

そのため、new()PSMethodNameプロパティは.NET IL (中間言語)のコンストラクタメソッドである.ctorとなっている。

PS>[string]::new|Get-MemberTypeName:System.Management.Automation.PSMethodNameMemberTypeDefinition------------------------CopyMethodSystem.Management.Automation.PSMemberInfoCopy()EqualsMethodboolEquals(System.Objectobj)GetHashCodeMethodintGetHashCode()GetTypeMethodtypeGetType()InvokeMethodSystem.ObjectInvoke(ParamsSystem.Object[]arguments)ToStringMethodstringToString()IsInstancePropertyboolIsInstance{get;}MemberTypePropertySystem.Management.Automation.PSMemberTypesMemberType{get;}NamePropertystringName{get;}OverloadDefinitionsPropertySystem.Collections.ObjectModel.Collection[string]OverloadDefinitions{get;}TypeNameOfValuePropertystringTypeNameOfValue{get;}ValuePropertySystem.ObjectValue{get;set;}PS>[string]::new.name.ctor

※通常、メソッド名とNameプロパティは一致している。

PS>[string]::Compare.nameComparePS>[string]::Concat.nameConcat

暗黙の型変換があるのでメソッドの引数はある程度柔軟に記述出来る。

明示的にキャストしなくても動作する
PS>'Bar'.GetType()IsPublicIsSerialNameBaseType----------------------------TrueTrueStringSystem.ObjectPS>[char[]]'Bar'BarPS>[string]::new([char[]]'Bar')BarPS>[string]::new('Bar')Bar

連想配列からキャストする方法

引数なしコンストラクタがある場合、連想配列からインスタンスを生成出来る。
この方法を使うとそのクラスのプロパティを補完入力できる。

process.png

プロパティを複数設定する場合は改行するか;で区切る。
改行だけでも動作はするが、補完入力は;で区切る時のみ有効になる。

SharedScreenshot.png

New-Objectを使う方法

次のような場面で使用する。

  • Comobjectを扱う場合...面倒な部分をラップしてくれる。
  • 引数ありコンストラクタとプロパティの設定を同時に行う場合
# @を書き忘れるとエラーになるPS$WshShell=New-Object-ComObjectWScript.Shell-Property{CurrentDirectory="D:\"}New-Object:Cannotbindparameter'Property'.Cannotconvertthe"CurrentDirectory="D:\""valueoftype"System.Management.Automation.ScriptBlock"totype"System.Collections.IDictionary".PS$WshShell=New-Object-ComObjectWScript.Shell-Property@{CurrentDirectory="D:\"}PS$WshShell.CurrentDirectoryD:\

usingについて

公式ドキュメント:about_Using - PowerShell | Microsoft Docs

Powershellのusingは3つの使い方がある。

usingnamespace<.NET-namespace># 指定したnamecpaceを省略出来るようになるusingmodule<module-name># Powershellモジュールで定義されたクラスを利用出来るようにするusingassembly<.NET-assembly-path># 指定したアセンブリのクラスを継承したクラスを作成するために使う

using assemblyclass構文で使われる。

Genericクラスの書き方

PS>usingnamespaceSystem.Collections.GenericPS>$list=[List[int]]::new()PS>$list.add(12)PS>$list12PS>$dic=[Dictionary[string,System.Diagnostics.Process]]::new()PS>$dic.Count0PS>Get-Process|ForEach-Object{$dic.TryAdd($_.ProcessName,$_)>$null}PS>$dic.Count140

Genericメソッドの書き方

PowershellではサポートしていないのでMethodInfo.MakeGenericMethod(Type[])を利用する。

参考
MethodInfo.MakeGenericMethod(Type[]) メソッド (System.Reflection) | Microsoft Docs
vors/GenericMethods.ps1

PS>$OfTypeInt=[System.Linq.Enumerable].GetMethods().where{$_.IsGenericMethod-and$_.name-eq'Oftype'}.MakeGenericMethod([int])PS>$OfTypeInt.Invoke($null,(,@(1,2,'a')))12PS>$OfTypeInt.Invoke($null,@(1,2,'a'))MethodInvocationException:Exceptioncalling"Invoke"with"2"argument(s):"Parameter count mismatch."

実践~MSDNの記事を利用する~

お題はFormの記事のサンプルコード
Form クラス (System.Windows.Forms) | Microsoft Docs

その1:そのままPowershellに置き換える

new()using namespaceでほぼ公式通りに記述することが可能。

usingnamespaceSystem.DrawingusingnamespaceSystem.Windows.FormsAdd-Type-AssemblyNameSystem.Windows.Forms# コンストラクタ# Form作成$form1=[Form]::new()# ボタン作成$button1=[Button]::new()$button2=[Button]::new()# ボタンの設定# サンプルコードとの違い…DialogResultは明示的に設定$button1.Text="OK"$button1.DialogResult=[DialogResult]::OK$button1.Location=[Point]::new(10,10)$button2.Text="Cancel"$button2.DialogResult=[DialogResult]::Cancel$button2.Location=[Point]::new($button1.Left,$button1.Height+$button1.Top+10)# Formの設定$form1.Text="My Dialog Box"$form1.HelpButton=$true$form1.FormBorderStyle=[FormBorderStyle]::FixedDialog$form1.MaximizeBox=$false$form1.MinimizeBox=$false# 生成したボタンを設定$form1.AcceptButton=$button1$form1.CancelButton=$button2# 表示位置$form1.StartPosition=[FormStartPosition]::CenterScreen# 合体$form1.Controls.Add($button1)$form1.Controls.Add($button2)# 表示$form1.ShowDialog()

その2:Powershellで書きやすいように書く

連想配列を使ったオブジェクト生成を利用するとプロパティ設定をまとめやすい。
AddRange()が使える場合は書き換えを検討する。

usingnamespaceSystem.DrawingusingnamespaceSystem.Windows.FormsAdd-Type-AssemblyNameSystem.Windows.Forms$button1=[Button]@{Text="OK";DialogResult=[DialogResult]::OK;Location=[Point]::new(10,20);}$button2=[Button]@{Text="Cancel";DialogResult=[DialogResult]::Cancel;Location=[Point]@{X=$button1.Left;Y=$button1.Height+$button1.Top+10;};}$form1=[Form]@{Text="My Dialog Box";HelpButton=$true;FormBorderStyle=[FormBorderStyle]::FixedDialog;MaximizeBox=$false;MinimizeBox=$false;StartPosition=[FormStartPosition]::CenterScreen;AcceptButton=$button1;CancelButton=$button2;}$form1.Controls.AddRange(@($button1,$button2))$form1.ShowDialog()

インテリセンスが効かなくなるが;が無くても動作する。

usingnamespaceSystem.DrawingusingnamespaceSystem.Windows.FormsAdd-Type-AssemblyNameSystem.Windows.Forms$button1=[Button]@{Text="OK"DialogResult=[DialogResult]::OKLocation=[Point]::new(10,20)}$button2=[Button]@{Text="Cancel"DialogResult=[DialogResult]::CancelLocation=[Point]@{X=$button1.LeftY=$button1.Height+$button1.Top+10}}$form1=[Form]@{Text="My Dialog Box"HelpButton=$trueFormBorderStyle=[FormBorderStyle]::FixedDialogMaximizeBox=$falseMinimizeBox=$falseStartPosition=[FormStartPosition]::CenterScreenAcceptButton=$button1CancelButton=$button2}$form1.Controls.AddRange(@($button1,$button2))$form1.ShowDialog()

semicolon.png
nosemicolon.png

Powershellだと難しいこと

次のような場合は再現は難しくなったり面倒になったりする。
IL、リフレクションを使えば大体のことは出来るが手間がかかる。

  • ラムダ式…Powershellにはラムダ式はないため。なお、Linqは静的メソッドで利用可能。
  • 自動生成コードが仕事をしている場合。WPFなどが該当。裏で自動生成されている箇所も手動で処理する必要がある。そういう部分をラップするのもPowershellモジュールの役目。

参考

High Performance PowerShell with LINQ
PowershellでLinqするためのレシピ。個人的にSum()をよく使う。(Measure-Objectよりも簡潔に書けるから)

終わりに

  • Powershellの生命線は補完機能だと思っている。
  • 機会があったらRegister-ArgumentCompleterについて掘り下げてみたい。

公式ドキュメント:Register-ArgumentCompleter (Microsoft.PowerShell.Core) - PowerShell | Microsoft Docs

次回があったら書きたいこと

イベント、BackgroundJobといった非同期の処理。
鍵を握るのは[scriptblock]と型変換。

※他の有力候補

  • VScodeでPowershellの開発コンテナを立ち上げる
  • VScodeでPowershellのハイブリッドモジュールを作成する
  • PowershellとWPFとXAML、あとバインド

Viewing all articles
Browse latest Browse all 9571

Trending Articles