LoginSignup
0
0

More than 3 years have passed since last update.

PowerShell パイプラインと配列部分式演算子

Last updated at Posted at 2021-01-20

はじめに

.NET は10年以上前にかじった程度なので構文に自信がない上、PowerShellはJavaと比べて個人的に理解しづらいなぁ・・・と悩む日々を送りながらも、なんだかんだ最近PowerShellにはまってます。

特にPowerShellのパイプライン処理、簡潔でいいですねぇ~。
使っていくうちに、JavaのStream APIの方が冗長なんじゃねぇか?と錯覚するような気分に陥っていきます。

ただそのパイプライン処理にかける配列の要素数と配列部分式演算子@()の関係で苦しんだことがあるので、これから述べていきます。

要素数が2以上のとき

要素数が2以上のときは、次のコードのように配列部分式演算子なんか気にせずに記述しても期待どおりの出力が得られます。

$array = "hoge", "huga"
$array1 = $array | % { $_ }
$array3 = if ($array) { $array | % { $_ } }
echo ($array1.Length, $array1[0] -join ",") # 出力:2,hoge
echo ($array3.Length, $array3[0] -join ",") # 出力:2,hoge

要素数が1のとき

問題はこのケースです。前述したノリでそのまま処理しようとすると・・・、

失敗例
[array]$array = "hoge"
$array1 = $array | % { $_ }
$array3 = if ($array) { $array | % { $_ } }
echo ($array1.Length, $array1[0] -join ",") # 出力:4,h
echo ($array3.Length, $array3[0] -join ",") # 出力:4,h

こうなります。出力が1,hogeとはなりません。
これは、パイプライン処理全体を配列部分式演算子で囲めばうまくいきます。ただし、if文やif-else文などの制御構文のブロック内にパイプライン処理が存在する場合は、その制御構文全体を囲まないとうまくいきません1

成功例
[array]$array = "hoge"
$array2 = @($array | % { $_ })
$array4 = @(if ($array) { $array | % { $_ } })
echo ($array2.Length, $array2[0] -join ",") # 出力:1,hoge
echo ($array4.Length, $array4[0] -join ",") # 出力:1,hoge

なぜこうなるの?

出力が4,hと出てくるものだから、てっきり$array配列の要素が一文字ずつ分解されて、長さ4の新たな配列が生成されたのかと思っていました。

ただ、echo $array1.GetType()を実行してみると・・・

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

・・・これはつまり、文字列ですけぇ?
パイプラインは要素1の奴が来ると、配列を文字列にしてしまうのか・・・。
更に、文字列に添字というちょっとよくわからない概念をいきなり突きつけられたので、試しに適当な文字列で試してみたら、

echo "hoge"[1] # 出力:o

何だこれは。
まるでJavaのString#charAtメソッドみたい。

添字って、配列の要素のインデックスのことじゃないのか?
それとも、これはそもそも添字とは違う概念なのか・・・?
はたまた、PowerShellではこれも添字とするのか・・・?
いまいち、整理がつきません。

追記

@ktz_alias さんより、自動ラップ・アンラップに関する情報をご提供いただきました。ありがとうございます!

パイプラインの出力結果を代入する変数に対し明示的に型指定を行うことでも、String型へのアンラップを防げるとのことでした。

[array]$array = "hoge"
[array]$array1 = $array | % { $_ }
[array]$array3 = if ($array) { $array | % { $_ } }
echo ($array1.Length, $array1[0] -join ",") # 出力:1,hoge
echo ($array3.Length, $array3[0] -join ",") # 出力:1,hoge

更に、パイプラインに流し込むと、暗黙的に配列にラップされるようです。パイプラインは如何なるときも必ず配列を処理するということなのでしょうかね。

$array = "hoge"
echo $array | % { $array.GetType() }
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

  1. このとき、制御構文のブロック内にあるパイプライン処理全体が配列部分式演算子で囲まれているかどうかによって、出力結果が変わることはないようです。 

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0