PowerShellの関数の戻り値に関する2つの問題

[PowerShellの関数の戻り値に関する2つの問題:目次]


あー!イライラするー!💢

山田かねすけ
山田かねすけ
けいぞう
けいぞう

どしたの?

なんか全然思った通り動いてくんないんだよな!PowerShellの野郎!💢

もしかして「関数の戻り値の扱い」に苦労してる?

標準出力やAddメソッドの結果が戻り値に含まれる

そう!なぜわかった!?
こんなコード書いてるんだけど、、何が悪いんだ?

山田かねすけ
山田かねすけ
function func {
    $dataTable = New-Object System.Data.DataTable
    $dataTable.Columns.Add("columns1")
    $newRow = $dataTable.NewRow()
    $newRow.columns1 = "abc"
    $dataTable.Rows.Add($newRow)

    Write-Output "hogehoge"

    return $dataTable
}

$result = func

if($result.Rows.Count -eq 1){
    Write-Output "1行だぞ"
}else{
    Write-Output "1行じゃないぞ"
}

1回だけAddでRowを追加したから、1行のDataTableが戻り値になるはずなのに。
「1行じゃないぞ」の方が出力されちゃうんだよな。。

そりゃそうだよ。
PowerShellの関数の戻り値には「標準出力」とかreturnに指定したオブジェクト以外も含まれるんだよ。

どういうことなんだ!?詳しく教えてくれよおお!!

(面倒くさいなあ)
とりあえず、その$resultの中身を見てみてよ。

おお、そうだった。
デバッグの基本はプリントデバッグだな。

山田かねすけ
山田かねすけ


AllowDBNull : True
AutoIncrement : False
AutoIncrementSeed : 0
AutoIncrementStep : 1
Caption : columns1
ColumnName : columns1
Prefix :
DataType : System.String
DateTimeMode : UnspecifiedLocal
DefaultValue :
Expression :
ExtendedProperties : {}
MaxLength : -1
Namespace :
Ordinal : 0
ReadOnly : False
Table : {System.Data.DataRow}
Unique : False
ColumnMapping : Element
Site :
Container :
DesignMode : False

hogehoge
columns1 : abc

思ってたのと全然違う!!!

$resultのカウントと型も見てみてよ。

こうか?

> $result.Count
3

> $result.GetType()
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True Object[] System.Array

型名が「Object[]」でカウント「3」?
DataTableが1つ返ってきてるだけじゃないみたいだな。

returnで指定したDataTable以外に2つが戻り値に含まれてるのが分かるよね。

hogehogeと、何かよく分からんデカいオブジェクトが余計だな!!

そう。よく分からんデカいオブジェクトはAddメソッドの結果だよ。それも戻り値に含まれるんだ。

意味不明だな!!!


PowerShell関数の戻り値に含まれるもの

  • returnに指定したオブジェクト
  • 標準出力の結果
  • Addメソッドの結果

戻り値に含みたくない場合はどうすればいいんだ!?
教えてくれよ!!!

パイプ(|)で繋いで「Out-Null」を付ければいいんだよ。

たったそれだけでいいのか!!
こうか?

function func {
    $dataTable = New-Object System.Data.DataTable
    $dataTable.Columns.Add("columns1") | Out-Null
    $newRow = $dataTable.NewRow()
    $newRow.columns1 = "abc"
    $dataTable.Rows.Add($newRow)

    Write-Output "hogehoge" | Out-Null

    return $dataTable
}

$result = func

if($result.Rows.Count -eq 1){
    Write-Output "1行だぞ"
}else{
    Write-Output "1行じゃないぞ"
}

とりあえずOK。$resultを見たら欲しいものだけになってるはずだよ。でもあともう一個直さないと・・・

>>$result

columns1
————–
abc

おーう!できたできた!!これこれ!!

DataTableの行数によって戻り値の型が異なる

ん?まだオカシイ!やっぱり出力結果が
「 1行じゃないぞ 」になるじゃねえか!

(話を最後まで聞いてよ)
型を見てみてよ。

>>$result.GetType()
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True False DataRow System.Object

DataRowになってる!!
そりゃDataRowに対してRowsを取ろうとしてもそんなものは無いからな・・・でもエラーにはならないのか!

戻り値がDataTable1つだけだった場合、DataTableが2行以上なら「Object[]」、1行なら「DataRow」、0行なら「null」として返されるんだ。

意味不明だな!!!

関数の戻り値がDataTableだけの場合

  • 2行以上なら「Object[]」として返される
  • 1行なら「DataRow」として返される
  • 0行なら「null」として返される

2行以上のときはGetType()でObjectになっていることを確認できるけど、実体としてはDataTableだから、この時だけは山田君の書き方が有効なんだ。
➡$result.Rows.Count

クソ面倒くさいけど分かったぞ!
つまり行数の判断の前に、型の判断をワンクッション挟めばいいんだな!

function func {
    $dataTable = New-Object System.Data.DataTable
    $dataTable.Columns.Add("columns1") | Out-Null
    $newRow = $dataTable.NewRow()
    $newRow.columns1 = "abc"
    $dataTable.Rows.Add($newRow)

    return $dataTable
}

$result = func

if($result.GetType().Name -eq "DataRow")
{  
    Write-Output "1行だぞ"
}
elseif($result -eq $null)
{
    Write-Output "0行だぞ"
}
elseif($result.Rows.Count -gt 1)
{
    $message = $result.Rows.Count.ToString() + "行だぞ"
    Write-Output $message
}
else
{
    Write-Output "不明だぞ"
}

よし!「1行だぞ」になった!
ついでに2行以上のときはちゃんと行数も出力してくれるようにしといた!
やっぱり俺は天才プログラマーだな!ワッハッハ!

僕が教えてあげただけなのに・・・

【 まとめ 】PowerShellの戻り値をreturn値だけにしたかったら「Out-Null」を使う

  • PowerShellの関数の戻り値にはreturnで指定した値と標準出力とAdd関数の処理結果などが複数含まれる
  • 標準出力などを戻り値に含めたくない場合は「Out-Null」を付ける
  • 戻り値のDataTableは行数によって異なる型で返される

山田君のDataTableの型の判断はちょっとダサいので、関数の外にDataTableの結果確認用の変数を用意してそこに入れるなど、色々と工夫ができそうですね。