まず、これが私の質問につながるので、PowerShellでXMLをかなり使用したこと、およびXMLファイルからカスタムオブジェクトの配列にデータをすばやく読み込む方法が好きだということから始めます。たとえば、次のXMLファイルがある場合:
<stuff>
<item name="Joe" age="32">
<info>something about him</info>
</item>
<item name="Sue" age="29">
<info>something about her</info>
</item>
<item name="Cat" age="12">
<info>something else</info>
</item>
</stuff>
そして、次のように単純に読むと:
[xml]$myxml = Get-Content .\my.xml
次に、次のようにアイテムの配列にアクセスできます。
[array]$myitems = $myxml.stuff.Item
$myitems
name age info
---- --- ----
Joe 32 something about him
Sue 29 something about her
Cat 12 something else
だから、今私の質問:
カスタムオブジェクトの配列の同様の構造を作成し、スクリプトで初期化するには、withoutファイルを読み取るにはどうすればよいですか? =
私は多くのループを作成したり、個々のオブジェクトを作成/初期化したりして、一度に1つずつ配列に追加することができます...
しかし、この作成/初期化をより簡単な方法で実行する方法があるはずです。ここで重要なのは、カスタムオブジェクトに3つ以上の要素があることです(そうでない場合は、ハッシュを使用していました)。
大きな文字列のXMLを作成し、Select-XMLを使用することも検討しましたが、構文を正しく理解できませんでした(それが正しい道でさえある場合)。
私はこれらの線に沿って何かをしたいと思います:
$myitems =
@([pscustomobject]@{name="Joe";age=32;info="something about him"},
[pscustomobject]@{name="Sue";age=29;info="something about her"},
[pscustomobject]@{name="Cat";age=12;info="something else"})
これはPowerShell 3でのみ機能することに注意してください。ただし、質問でバージョンについて言及しなかったため、これは問題ではないと思います。
更新
コメントで言及されているのは、以下を行う場合です。
$younger = $myitems | Where-Object { $_.age -lt 20 }
Write-Host "people younger than 20: $($younger.Length)"
予想どおり1
は取得できません。これは、単一のpscustomobject
が返されたときに発生します。これは、Length
およびCount
のサロゲートプロパティがあるため、PowerShellの他のほとんどのオブジェクトでは問題になりません。残念ながらpscustomobject
はそうではありません。これはPowerShell 6.1.0で修正されました。これを回避するには、演算子@()
を使用します。
$younger = @($myitems | Where-Object { $_.age -lt 20 })
詳細な背景については、 here および here を参照してください。
更新2
PowerShell 5では、 Classes を使用して同様の機能を実現できます。たとえば、次のようなクラスを定義できます。
class Person {
[string]$name
[int]$age
[string]$info; `
`
Person(
[string]$name,
[int]$age,
[string]$info
){
$this.name = $name
$this.age = $age
$this.info = $info
}
}
ここでのバックティックは、コマンドラインにコピーアンドペーストできるようにするためのものであり、スクリプトでは必要ありません。クラスが定義されると、通常の方法で配列を作成できます。
$myitems =@([Person]::new("Joe",32,"something about him"),
[Person]::new("Sue",29,"something about her"),
[Person]::new("Cat",12,"something else"))
この方法には、PowerShell 5であっても、以前の更新で言及された欠点がないことに注意してください。
Update 3
最初の例と同様に、ハッシュテーブルを使用してクラスオブジェクトを初期化することもできます。そのためには、デフォルトのコンストラクターが定義されていることを確認する必要があります。コンストラクターを提供しない場合、コンストラクターが作成されますが、デフォルト以外のコンストラクターを提供する場合、デフォルトのコンストラクターは存在せず、明示的に定義する必要があります。自動作成されるデフォルトコンストラクターの例を次に示します。
class Person {
[string]$name
[int]$age
[string]$info;
}
それにより、次のことができます。
$person = @([Person]@{name='Kevin';age=36;info="something about him"}
[Person]@{name='Sue';age=29;info="something about her"}
[Person]@{name='Cat';age=12;info="something else"})
これはもう少し冗長ですが、もう少し明示的です。これを指摘してくれた@ js2010に感謝します。
PowerShellでカスタムオブジェクトの配列を初期化する簡単な方法を次に示します。
> $body = @( @{ Prop1="1"; Prop2="2"; Prop3="3" }, @{ Prop1="1"; Prop2="2"; Prop3="3" } )
> $body
Name Value
---- -----
Prop2 2
Prop1 1
Prop3 3
Prop2 2
Prop1 1
Prop3 3
たぶん、あなたはこのような意味ですか?オブジェクトを作成してFormat-Tableを使用するのが好きです:
PS C:\Users\Joel> $array = @()
PS C:\Users\Joel> $object = New-Object -TypeName PSObject
PS C:\Users\Joel> $object | Add-Member -Name 'Name' -MemberType Noteproperty -Value 'Joe'
PS C:\Users\Joel> $object | Add-Member -Name 'Age' -MemberType Noteproperty -Value 32
PS C:\Users\Joel> $object | Add-Member -Name 'Info' -MemberType Noteproperty -Value 'something about him'
PS C:\Users\Joel> $array += $object
PS C:\Users\Joel> $array | Format-Table
Name Age Info
---- --- ----
Joe 32 something about him
これにより、プロパティに応じて、配列内のすべてのオブジェクトが列に配置されます。
ヒント:-auto
を使用すると、テーブルのサイズが改善されます
PS C:\Users\Joel> $array | Format-Table -Auto
Name Age Info
---- --- ----
Joe 32 something about him
テーブルで必要なプロパティを指定することもできます。各プロパティ名をカンマで区切るだけです:
PS C:\Users\Joel> $array | Format-Table Name, Age -Auto
Name Age
---- ---
Joe 32
配列を初期化する最も簡単な方法
配列の作成
$array = @()
ヘッダーを作成する
$line = "" | select name,age,phone
行を埋める
$line.name = "Leandro"
$line.age = "39"
$line.phone = "555-555555"
$ arrayに行を追加
$array += $line
結果
$array
name age phone
---- --- -----
Leandro 39 555-555555
以下は、NoteProperty識別子と[pscustomobject]
- castの繰り返しを避ける、受け入れられた回答のより簡潔なバージョンです。
$myItems = ("Joe",32,"something about him"), ("Sue",29,"something about her")
| ForEach-Object {[pscustomobject]@{name = $_[0]; age = $_[1]; info = $_[2]}}
結果:
> $myItems
name age info
---- --- ----
Joe 32 something about him
Sue 29 something about her
「Here-String」を使用してXMLにキャストします。
[xml]$myxml = @"
<stuff>
<item name="Joe" age="32">
<info>something about him</info>
</item>
<item name="Sue" age="29">
<info>something about her</info>
</item>
<item name="Cat" age="12">
<info>something else</info>
</item>
</stuff>
"@
[array]$myitems = $myxml.stuff.Item
$myitems
上記のデータを考えると、これは私がそれを行う方法です:
# initialize the array
[PsObject[]]$people = @()
# populate the array with each object
$people += [PsObject]@{ Name = "Joe"; Age = 32; Info = "something about him" }
$people += [PsObject]@{ Name = "Sue"; Age = 29; Info = "something about her" }
$people += [PsObject]@{ Name = "Cat"; Age = 12; Info = "something else" }
以下のコードは、Where-Object
の後にアイテムが1つしかない場合でも機能します。
# display all people
Write-Host "People:"
foreach($person in $people) {
Write-Host " - Name: '$($person.Name)', Age: $($person.Age), Info: '$($person.Info)'"
}
# display with just 1 person (length will be empty if using 'PSCustomObject', so you have to wrap any results in a '@()' as described by Andrew Savinykh in his updated answer)
$youngerPeople = $people | Where-Object { $_.Age -lt 20 }
Write-Host "People younger than 20: $($youngerPeople.Length)"
foreach($youngerPerson in $youngerPeople) {
Write-Host " - Name: '$($youngerPerson.Name)'"
}
結果:
People:
- Name: 'Joe', Age: 32, Info: 'something about him'
- Name: 'Sue', Age: 29, Info: 'something about her'
- Name: 'Cat', Age: 12, Info: 'something else'
People younger than 20: 1
- Name: 'Cat'
クラスの少しのバリエーション。ハッシュテーブルで初期化します。
class Point { $x; $y }
$a = [Point[]] (@{ x=1; y=2 },@{ x=3; y=4 })
$a
x y
- -
1 2
3 4
$a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Point[] System.Array
$a[0].gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False Point System.Object
定義済みの型の配列を作成する必要があり、次のように成功しました。
[System.Data.DataColumn[]]$myitems = ([System.Data.DataColumn]("col1"),
[System.Data.DataColumn]("col2"), [System.Data.DataColumn]("col3"))