🚂

[PowerShell]標準出力を無視するコマンドに変換するFunction

2024/02/20に公開

概要

戻り値があるFunction内で標準出力の伴うコマンドを実行する場合は、PowerShellの制約上、そのコマンドの出力を無視する必要があります。

※ 参考記事:PowerShell 関数の return について - マイクロソフ党ブログ

PowerShellでFunctionを作成する際、標準出力を無視するコマンド形式に置き換える為、キータイプやテキストエディターの正規表現などを活用し対応してきましたが、ソースコードに量があると対応が面倒です。

今回は対象のコマンドが入っている文字列配列を渡すと、標準出力を無視するコマンドに変換するFunctionを作成してみました。

この記事のターゲット

  • PowerShellユーザーの方
  • 標準出力するコマンドの出力を無視するよう一括変換したい方
  • その他の用途として先頭、または末尾に指定の文字列をいれたい方

標準出力を無視する方法について

前述した通り、戻り値が設定されているFunction内で標準出力があるコマンドをそのままコーディングしてしまうと、
戻り値がSystem.Object[]というオブジェクト型の配列により、想定する戻り値になりません。

それを回避する為には標準出力を無視するコマンドへと変換が必要。

無視する方法は、複数ありますが、先頭に[void](、末尾に)を追加することでvoidキャストで標準出力を無視する方法を最初に説明します。

たとえば、New-Item -Path 'D:\Downloads\work_folder' -ItemType Directoryというフォルダーを作成するコマンドを実行すると、標準出力で下記のような結果が返ってきます。

標準出力の伴うNew-Itemコマンドレット
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にキャストする事で標準出力を無視する場合
[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にキャストする方法で変換。

Add-FixedString
# 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

参考情報

まとめ

  • 戻り値のあるFunction内で標準出力の伴うコマンドを実行する場合は、標準出力を無視する必要がある
  • 出力の無視は、環境にもよるが下記3つの方法を選択するのがよさそう
    • $nullにリダイレクトする方法
    • $nullに代入する方法
    • voidにキャストする方法
  • コマンドが入った文字列配列を渡すと標準出力を無視するコマンドに変換するFunctionを作成した

関連記事

https://haretokidoki-blog.com/pasocon_powershell-startup/
https://zenn.dev/haretokidoki/articles/7e6924ff0cc960

GitHubで編集を提案

Discussion