Skip to content

MS_PowerShellObjectBasedShell

nishi_74322014 edited this page Aug 31, 2026 · 1 revision

PowerShell オブジェクトベースのシェル

概要

  • 以前から、オブジェクトベースのプログラム、スクリプトWSH、perl)などは存在したが、

  • PowerShell が唯一のオブジェクト・ベースのシェル(perl に似ている)である。

  • # OS レイヤから少々遠いためシェルというよりシェル・スクリプトという意見もある。

詳細

テキストベースからオブジェクトベースへ。

皆、

違いに混乱する。

  • パイプラインで渡せるオブジェクト型がある。

  • パイプラインで何が渡るかは Get-Member コマンドレットで調べる
    http://mojibake.seesaa.net/article/53585441.html

    • シェルだが、プログラム、スクリプト(perl)のような仕様。

    • 変数、配列、Property、Method(.NET ライク)、また、
      無名関数、delegate、検索条件をラムダ式的に・・・なども可能。

# Like This
#
#  ↓パイプ    ↓ラムダ式的な
   get-service | where-object {$_.Status -eq "Running"}
#
#  パイプしなくてもキャスト(暗黙)でつなげられる。

移行メモ(正誤): 元ページの「Get-Memberコマンドレット調べる」は
Get-Member コマンドレットで調べる」の脱字と判断し、補った。

オブジェクトのパイプラインとは?

  • Unix / Linux の shell コマンドや、Windows のコマンドプロンプトでも、
    |」を使って、コマンドを複数並べてパイプラインが記述できる。

  • 内部的には、標準出力のテキストを次のコマンドの標準入力へ渡している。

  • PowerShell では、PowerShell のオブジェクトをパイプラインで渡すことができる。

    • そして、ほとんどの PowerShell コマンドがオブジェクトを出力する。
    • また、オブジェクトには、多くのプロパティを含んでいる。
    • 後処理のために、オブジェクトにプロパティを追加できる。
    • プロパティを判定してフィルタしたり、プロパティを使ってソートできる。
    • 最後に Format-ListFormat-Table などのコマンドを使って、
      一部のプロパティを整形して表示できる。
  • .ps1 スクリプト内で定義する関数(function)や、自作の Cmdlet でも、
    オブジェクトを返すようにしておくと、再利用し易くなる。

  • パイプラインの例は、後述のオブジェクトへメンバー追加の例を参照。

  • パイプラインの詳細は、PowerShell コマンドプロンプトで get-help about_pipeline を参照。

補足(この違いが実務で効く場面): 「テキストか、オブジェクトか」の差は、
後段の処理の壊れやすさとして現れる。

  • テキストベースgrep / awk / findstr
    出力の書式に依存する。列の位置や区切り文字が変わると壊れる。
    ロケールによって日付や桁区切りの表記が変わることも影響する。
  • オブジェクトベース
    プロパティ名に依存する。表示の書式が変わっても影響を受けない。

したがって、シェル(スクリプト)の移行
CMD / WSH から PowerShell へ移す際は、
for /f によるテキスト解析をプロパティ参照に置き換えるのが要点になる。

補足(Format-* はパイプラインの終端に置く): 初心者が最も引っかかる点として、
Format-Table / Format-List を通した後のオブジェクトは、
元のオブジェクトではなく整形用のオブジェクトになる
という仕様がある。

# NG: Format-Table の後で Where-Object しても意図どおり動かない
Get-Service | Format-Table | Where-Object { $_.Status -eq "Running" }

# OK: 絞り込んでから整形する
Get-Service | Where-Object { $_.Status -eq "Running" } | Format-Table

本ページが「最後に Format-ListFormat-Table などのコマンドを使って」
と書いているのは、まさにこの順序を指している。
なお、後続処理でデータとして使うなら
Select-Object / ConvertTo-Json / Export-Csv を使う。

オブジェクトへメンバー追加する。

  • PowerShell では、Add-Member を使って、
    オブジェクトに動的にメンバーを追加できる。

  • OS や他のライブラリが返したオブジェクトに、情報を追加しながら、
    パイプラインを使って、順次処理する。といったことができる。

スクリプト

function dirinfo {
  param(
   [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
   [object]$dir,

   [boolean]$totalup = $false
  )

  # 空っぽの配列
  $dirs = @()

  # $dir へ プロパティ追加
  $dir | Add-Member -MemberType NoteProperty -Name TotalFiles -Value 0
  $dir | Add-Member -MemberType NoteProperty -Name TotalSize -Value 0

  foreach ($f in (get-childitem $dir.FullName)) {
    if ( $f.Attributes -band [System.IO.FileAttributes]::Directory ) {
      # サブフォルダの情報
      $ret = dirinfo $f $totalup
      if ( $totalup ) {
        # 末尾の情報を $dir へ加算
        $dir.TotalFiles += $ret[-1].TotalFiles
        $dir.TotalSize += $ret[-1].TotalSize
      }
      $dirs += $ret   # サブフォルダ全体を覚える
    } else {
      $dir.TotalFiles += 1
      $dir.TotalSize += $f.Length
      $dirs += $f
    }
  }
  $dirs += $dir   # $dir自身を末尾に覚える

  # 覚えたものをまとめて返す
  write-output $dirs
}

実行例

  • 実行例のパイプライン
dirinfo (get-item "c:\program files") | sort-object totalsize -Descending |
    where { $_.TotalSize -gt 1 } | select-object -first 5 |
    format-list -Property TotalFiles, TotalSize, Fullname

(行は折り返して表示しています。実行する際は、1 行に入力してください。)

  • ココでは、以下のことをしている。

    1. get-itemC:\Program Files フォルダのオブジェクトを取得して、
    2. dirinfo でサブフォルダのサイズを集計、
    3. sort-objectTotalSize の大きい順に並び替えて、
    4. whereTotalSize が 1 以上のオブジェクトだけ抽出して、
    5. select-object で 最初の 5 個だけ取り出し、
    6. format-list で 特定のプロパティのみ表示。

移行メモ(正誤): 元ページの sort-obejctsort-object の誤記と判断し、修正した。

補足($dirs += $ret は要素数が増えると重い): PowerShell の配列は
固定長で、+= のたびに新しい配列を作り直している。
ファイル数が多いフォルダで上記を走らせると、
この行が支配的なコストになる。件数が多い場合は、

$dirs = [System.Collections.Generic.List[object]]::new()
$dirs.Add($f)

のように List<T> を使うか、パイプラインへ直接流す方が速い。
本ページの参考にある
「大量の出力を扱う場合は、オブジェクト・ベースが仇となる場合もある」
という指摘とも通じる話である。

参考

  • PowerShell:Windowsでおそらく最高のシェル
    http://programming-aip.blogspot.jp/2013/01/powershellwindows.html

    • 最近は、コマンドレットも充実してきて人気も出てきていると思う。

    • 分析中で面白いのは、
      「大量の出力を扱う場合は、オブジェクト・ベースが仇となる場合もある」
      という所である。

      • 理由は、大量テキスト処理可能な CUI の EXE にパイプさせれば上手く編集できるため。
      • しかし、そもそも、Windows の場合、強力なテキスト処理を実装する CUI の EXE が充実していない。

補足(この指摘の現在): 著者が挙げる「Windows には強力なテキスト処理の
CUI が充実していない」という状況は、その後変わっている。

  • WSL により grep / awk / sed / jq がそのまま使える
    (→ Bash
  • winget や Scoop で
    これらの Windows ネイティブ移植版を簡単に導入できる

つまり、現在は「構造化されたデータは PowerShell、
大量のテキストは Unix 系ツール
」と使い分けられる。
PowerShell 側も ConvertTo-Json / ConvertFrom-Json を備えているため、
オブジェクトを JSON にして jq へ渡すといった連携もしやすい。

本 Wiki 内


Tags: シェル, インフラストラクチャ, Windows

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally