配列_4(PowerShell_10)

この連載では、PowerShellについて学んでいきます。前回の記事はこちらになります。

hirocom777.hatenadiary.org

前回は配列(System.Arrayクラス)の静的メソッドについて学びました。

今回も配列です。配列の複製について見ていきます。簡単そうにみえるのですが、ちょっと奥が深いのです。

変数の値コピー

変数の値をコピーするには「=」演算子で変数同士を結べばよいです。

PS > $a = 1
PS > $b = $a
PS > $a , $b
1
1
PS > $a = 2
PS > $a , $b
2
1

コピー後にコピー元の変数を確認しても、コピー先の変数の値は変わりません。何故このような確認をしてみたかというと、配列では少し動作が異なるからです。

配列の場合

配列で同様の操作をして、挙動を見てみましょう。

PS > $a = @(1)
PS > $b = $a
PS > $a , $b
1
1
PS > $a[0] = 2
PS > $a , $b
2
2

コピー元の配列要素を変更すると、コピー先の配列も変更されます。これは、配列の場合「=」演算子で結ぶと参照先がコピーされるからです。

配列要素の値コピー

それでは配列要素の値をコピーする場合は、どうすればいいのでしょうか。この場合は、以下にご紹介するメソッドを使用します。

Cloneメソッド

Cloneメソッドを使用すると、配列要素の値をコピーできます。

PS > $a = @(1)
PS > $b = $a.Clone()
PS > $a , $b
1
1
PS > $a[0] = 2
PS > $a , $b
2
1

元の配列を変更してもコピー先の値はそのままです。

CopyToメソッド

CopyToメソッドを使用しても、配列要素の値をコピーできます。記述方法は以下の通りです。

コピー元配列.CopyTo(コピー先配列, コピー先開始位置)

コピー先の配列はあらかじめ用意する必要があります。また、コピーする開始位置を指定できます。

PS > $a = @(1 , 2)
PS > $b = @(0 , 0 , 0 , 0)
PS > $a.CopyTo($b , 1)
PS > $b
0
1
2
0

[Array]::Copy()

前回ご紹介した静的メソッドである「[Array]::Copy()」でもコピー可能です。詳細は前回を参照してください。

3つのメソッドは、以下のように使い分けます。

  • 全体の配列を丸ごと新しい配列に複製したい場合: Clone
  • 既存の配列に元の配列の内容をコピーして、特定の位置から上書きしたい場合: CopyTo
  • 部分的に、または異なる開始位置やコピーする要素数を指定してコピーしたい場合: 静的な [Array]::Copy

シャローコピーとディープコピー

これらのメソッドで行われるコピーはシャローコピー(浅いコピー)といいます。要素が配列の場合は参照先がコピーされるだけで、値はコピーされません。以下の例を見てみましょう。

PS > $a = @(@(1) , @(2) , @(3))
PS > $b = $a.Clone()
PS > $b
1
2
3
PS > $b[0][0]
1
PS > $b[0][0] = 2
PS > $a
2
2
3

コピーした変数の配列要素の値を変更すると、コピー元の配列要素も同様に変更されています。

参照先ではなく値を直接コピーする方法をディープコピー(深いコピー)といいます。 PowerShellにはディープコピーをするメソッド、コマンドレットがありません。ディープコピーするには、各要素を個別にコピーするしかありません(シリアライズ/デシリアライズを使用した方法があるようですが、難解なようですので割愛します)。

PS > $a = @(@(1) , @(2) , @(3))
PS > $b = @(0 , 0 , 0)
PS > $b[0] = $a[0]
PS > $b[1] = $a[1]
PS > $b[2] = $a[2]
PS > $b
1
2
3
PS > $b[0] = 5
PS > $b
5
2
3
PS > $a
1
2
3

配列の要素に配列を指定する方法は、避けたほうがいいみたいです。

二次元配列

二次元配列を作成するには、コマンドレット「New-Object」を使用します。New-Objectは、オブジェクトのインスタンスを作成します。以下は2×3の二次元配列の例です。

PS > $a = New-Object 'Int[,]' 2,3
PS > $a[0,0] = 1
PS > $a[0,1] = 2
PS > $a[0,2] = 3
PS > $a[1,0] = 4
PS > $a[1,1] = 5
PS > $a[1,2] = 6
PS > $a.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32[,]                                 System.Array

PS > $a
1
2
3
4
5
6

こちらも「=」演算子で結ぶと参照先がコピーされます。値のコピーはシャローコピーで行います。

PS > $b = $a
PS > $b[0,0]
1
PS > $a[0,0] = 2
PS > $b[0,0]
2
PS > $a[0,0] = 1
PS > $b = $a.Clone()
PS > $b[0,0]
1
PS > $a[0,0] = 2
PS > $b[0,0]
1

次回はパイプライン

いかがでしょうか。配列を扱う場合には、参照先のコピー、値のコピーの違いに注意したいですね。次回は関数です。今までは1行でひとつのコマンドを実行して、結果を出力していました。関数を使用すると複数のコマンドをまとめて実行、結果を取得したり、値を渡して実行できたりします。お楽しみに!!

hirocom777.hatenadiary.org

PowerShell基礎の連載はコチラから