[PowerShell]標準出力を無視するコマンドに変換するFunction
概要
戻り値があるFunction内で標準出力の伴うコマンドを実行する場合は、PowerShellの制約上、そのコマンドの出力を無視する必要があります。
※ 参考記事:PowerShell 関数の return について - マイクロソフ党ブログ
PowerShellでFunctionを作成する際、標準出力を無視するコマンド形式に置き換える為、キータイプやテキストエディターの正規表現などを活用し対応してきましたが、ソースコードに量があると対応が面倒です。
今回は対象のコマンドが入っている文字列配列を渡すと、標準出力を無視するコマンドに変換するFunctionを作成してみました。
この記事のターゲット
- PowerShellユーザーの方
- 標準出力するコマンドの出力を無視するよう一括変換したい方
- その他の用途として先頭、または末尾に指定の文字列をいれたい方
標準出力を無視する方法について
前述した通り、戻り値が設定されているFunction内で標準出力があるコマンドをそのままコーディングしてしまうと、
戻り値がSystem.Object[]
というオブジェクト型の配列により、想定する戻り値になりません。
それを回避する為には標準出力を無視するコマンドへと変換が必要。
無視する方法は、複数ありますが、先頭に[void](
、末尾に)
を追加することでvoidキャストで標準出力を無視する方法を最初に説明します。
たとえば、New-Item -Path 'D:\Downloads\work_folder' -ItemType Directory
というフォルダーを作成するコマンドを実行すると、標準出力で下記のような結果が返ってきます。
PS C:\Users\"ユーザー名"> New-Item -Path 'D:\Downloads\work_folder' -ItemType Directory
Directory: D:\Downloads
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2024/02/19 11:34 work_folder
PS C:\Users\"ユーザー名">
戻り値を設定しているFunction内で、上記のような標準出力のあるコマンドを実行する場合、
下記のように標準出力を無視するコマンドに変換が必要です。
[void](New-Item -Path 'D:\Downloads\work_folder' -ItemType Directory)
その他で標準出力を無視する方法
上記で紹介したvoidキャスト以外にも標準出力を無視する方法があります。
voidキャストを含め、4つの方法の特徴を紹介。
なお、それぞれの方法で標準出力は無視できますが、標準エラーは無視できません。
エラー出力も考慮する必要がある場合は、try-catchでエラー制御する方法や、
標準エラーも標準出力として渡しリターンコードで制御する方法などが考えられますが、
この記事では趣旨から脱線してしまうので割愛します。
-
voidにキャストする方法(説明済み)
-
特徴
コマンドの出力をvoid型にキャストすることで、標準出力を無視できる。例[void]("コマンド")
-
-
$nullにリダイレクトする方法
-
特徴
コマンドの出力を$null変数にリダイレクトすることで、標準出力を無視できる。例"コマンド" > $null
-
-
$nullに代入する方法
-
特徴
コマンドの出力を$null変数に代入することで、標準出力を無視できる。例$null = "コマンド"
-
-
パイプラインでOut-Nullに渡す方法
-
特徴
コマンドの出力をパイプラインでOut-Nullコマンドレットに渡すことで、標準出力を無視できる。例"コマンド" | Out-Null
-
各方法で性能テストしてみる < クリックで折りたたみが開く >
下記のコードで性能テストを実施してみました。
Function PerformanceTest_IgnoreOutput {
param(
[System.String]$command,
[System.Int32]$count
)
$results = @{}
# $nullにリダイレクトする方法のテスト
Write-Host "Testing redirecting to `$null..."
$start = Get-Date
for ($i = 0; $i -lt $count; $i++) {
Invoke-Expression "$command > `$null"
}
$end = Get-Date
$exe_time = $end - $start
Write-Host "Done. Execution Time: $exe_time"
Write-Host ''
$results["Redirecting to `$null"] = $exe_time
# コマンド結果を$nullに代入する方法のテスト
Write-Host "Testing assigning to `$null..."
$start = Get-Date
for ($i = 0; $i -lt $count; $i++) {
$null = Invoke-Expression $command
}
$end = Get-Date
$exe_time = $end - $start
Write-Host "Done. Execution Time: $exe_time"
Write-Host ''
$results["Assigning to `$null"] = $exe_time
# voidにキャストする方法のテスト
Write-Host "Testing casting to void..."
$start = Get-Date
for ($i = 0; $i -lt $count; $i++) {
[void](Invoke-Expression $command)
}
$end = Get-Date
$exe_time = $end - $start
Write-Host "Done. Execution Time: $exe_time"
Write-Host ''
$results["Casting to void"] = $exe_time
# パイプラインでOut-Nullに渡す方法のテスト
Write-Host "Testing piping to Out-Null..."
$start = Get-Date
for ($i = 0; $i -lt $count; $i++) {
Invoke-Expression $command | Out-Null
}
$end = Get-Date
$exe_time = $end - $start
Write-Host "Done. Execution Time: $exe_time"
Write-Host ''
$results["Piping to Out-Null"] = $exe_time
# テスト結果の表示
Write-Host "Test results:"
$results.GetEnumerator() | Sort-Object Value | Format-Table -AutoSize
}
PerformanceTest_IgnoreOutput 'Get-Process' 10000
PS C:\Users\"ユーザー名"> PerformanceTest_IgnoreOutput 'Get-Process' 10000
Testing redirecting to $null...
Done. Execution Time: 00:00:32.2138029
Testing assigning to $null...
Done. Execution Time: 00:00:32.1898434
Testing casting to void...
Done. Execution Time: 00:00:32.2642199
Testing piping to Out-Null...
Done. Execution Time: 00:00:37.1512322
Test results:
Name Value
---- -----
Assigning to $null 00:00:32.1898434
Redirecting to $null 00:00:32.2138029
Casting to void 00:00:32.2642199
Piping to Out-Null 00:00:37.1512322
PS C:\Users\"ユーザー名">
ハードウェアの性能、実行する処理、繰り返し回数など環境によってテスト結果は大きく変わると思いますが、
今回の1万回、Get-Process
コマンドレットを繰り返し実行してみた結果、下記3点はほぼ結果がかわりませんでした。
- $nullにリダイレクトする方法
- $nullに代入する方法
- voidにキャストする方法
4つの方法の中で ハイプでOut-Nullに渡す方法 だけは、上記3つの手法よりもすこし遅い事がわかります。
おそらく繰り返し回数や実行する処理が多くなるほど差は開くでしょう。
今回、作成したFunctionで変換する方法は、先頭と末尾に文字列を追加する必要で手動の変換が一番面倒なvoidにキャストする方法で変換してみます。
作成したFunction
文字列配列に対象のコマンドを入れて第1引数で渡す。
第2と第3引数で先頭と末尾に追加する文字列を渡し、それらを結合して返すFunction。
第2と第3引数については、任意。
他の文字列を入れたい場合は、引数で指定するとその文字で結合されます。
第2と第3に引数を指定しないと規定値でvoidにキャストする方法で変換。
# Functionの定義
function Add-FixedString {
Param (
# 必須パラメーター:コマンドが記載された文字列配列
[Parameter(Mandatory=$true)]
[System.String[]]$commands,
# オプションパラメーター:先頭に追加する文字
[System.String]$first = '[void](',
# オプションパラメーター:末尾に追加する文字
[System.String]$last = ')'
)
# 結果を格納する配列を作成
[System.String[]]$result = @()
# 配列内のコマンドを繰り返し処理で変更
[System.String]$modified = ''
foreach ($command in $commands) {
# 先頭と末尾に固定値の文字を追加
$modified = "$($first)$($command)$($last)"
# 結果の配列に追加
$result += $modified
}
# 結果の配列を返す
return $result
}
# 実行前の準備:変換対象のコマンドを文字列配列に代入
$commands = @(
'Get-Date',
'Get-Process',
'Get-ChildItem'
)
# 実行
# voidキャストに変換する場合
Add-FixedString -commands $commands
# 下記がコマンド結果。
# [void](Get-Date)
# [void](Get-Process)
# [void](Get-ChildItem)
# 任意の文字を先頭と末尾に追加する場合($nullにリダイレクトする方法)
Add-FixedString -commands $commands -first '' -last ' > $null'
# 下記がコマンド結果。
# Get-Date > $null
# Get-Process > $null
# Get-ChildItem > $null
参考情報
-
PowerShell の関数 (Function) の使い方 - マイクロソフ党ブログ
https://microsoftou.com/powershell-function -
PowerShell about_Return - Microsoft Learn
https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_return
まとめ
- 戻り値のあるFunction内で標準出力の伴うコマンドを実行する場合は、標準出力を無視する必要がある
- 出力の無視は、環境にもよるが下記3つの方法を選択するのがよさそう
- $nullにリダイレクトする方法
- $nullに代入する方法
- voidにキャストする方法
- コマンドが入った文字列配列を渡すと標準出力を無視するコマンドに変換するFunctionを作成した
関連記事
Discussion