トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

コーディングTips.ps1

[Windows PowerShell]

ファイルとディレクトリを区別する方法

Get-ChildItem でファイルやフォルダを列挙したとき、ファイルだけを対象、またはディレクトリだけを対象としたい。

PSIsContainerプロパティを使う

Get-ChildItem -r C:\TargetFolder | where { $_.PSIsContainer }

PSIsContainerはPowerShell上で参照できるプロパティ。
$True の場合はコンテナ(ディレクトリ)、$False の場合はコンテナ内のアイテム(ファイル)。

インスタンスの型チェックを使う

Get-ChildItem -r C:\TargetFolder | where { $_ -is [IO.FileInfo] }

-is 演算子で、オブジェクトが.NET Frameworkのある型である(またはその型を継承している)かどうか、を判定できる。
Get-ChildItemはファイルの場合は System.IO.FileInfo クラス、ディレクトリの場合はSystem.IO.DirectoryInfo クラスのインスタンスを返す。
Get-ChildItemがどんなオブジェクトを返してくるかは、Get-Member で調べることができる。
こちらの方が汎用性は高いと思われる。

.NET Frameworkの型名指定は角カッコ[]を使う(System名前空間は省略可能とのこと)。文字列で指定してもよいようだが、この方が型名をしていしていることが明示的なので、記法としてはよいと思う。

ファイルIO

PowerShellにはGet-Contentがあるので、テキストファイル処理のために.NET Frameworkクラスを使用する必要は通常ない。
しかし、たとえば System.Security.Cryptography で処理するため等で、生の FileStream オブジェクトインスタンスが欲しい場合がある。

$fs = New-Object IO.FileStream -ArgumentList "C:\target_file.txt", "Open", "Read"

ということで、コンストラクタへの引数を複数渡すときには -ArgumentList オプションが使える。
さらに、FileMode と FileAccess の列挙体の値を引数として渡すときに、それぞれの列挙体のメンバー名を文字列として渡せばよい。

これがわかれば以下の同義文も書ける。

$fs = [IO.System.File]::Open("C:\target_file.txt", "Open", "Read")

ところで、.NET Frameworkのクラスに渡すファイルパスが相対パスだった場合、それは$homeを基準に解釈されてしまう。
$pwdではないので、注意が必要。
これを補正するためには、コマンドレットを経由してフルパスを得るようにすればよい。コマンドレットは相対パスを$pwdを基準に解釈してくれる。

その1: Resolve-Pathでフルパス文字列を得る。

param ($FilePath)

$fullPath = Resolve-Path $FilePath
$fs = [IO.System.File]::Open($fullPath, "Open", "Read")

その2: Get-Itemを使ってFileInfoオブジェクトを取得する。FileInfoの他のプロパティも後で使いたいときなど。

param($FilePath)

$fi = Get-Item $FilePath
$fs = [IO.System.File]::Open($fi.FullName, "Open", "Read")

コマンドレットの標準エラー出力を捨てる

スクリプト中で try - catch を使ってエラー処理をしても、コマンドレットはエラー発生時には勝手に標準エラー出力に自分のエラーメッセージを吐き出してしまう。
これはかなりカッコ悪い。エラーメッセージを制御したいから try - catch を使っているのに、これでは意味がない。

そんなときには、コマンドレットの出力を $null にリダイレクトすることができる。
たとえば Resolve-Path は与えられたパスが存在しないとエラーを吐くが、これを catch してスクリプト独自のエラーメッセージを出したい場合。

param($Path)

try {
    $fullPath = Resolve-Path $Path 2> $null
} catch {
    Write-Output "My message is ... "
    ... 
}

これで Revolve-Path のエラーメッセージは捨てられて、catch節に制御が移る。
エラーが発生しなければ、$fullPath に Resolve-Path の結果が代入される。

スクリプトの実行停止

[System.Environment]::Exit(0)