[PowerShell]定義したFunctionの中身を確認する5つの方法(4つのコマンドと自作Function)
概要
PowerShellを使っていて何回もFunctionの検証していると、現時点の定義を確認したい事が頻繁にあり、
その方法を調べたので紹介します。
この記事のターゲット
- PowerShellユーザーの方
- コマンドレットでFunction定義を確認する方法
-
Get-ChildItem
コマンドレットを使用した2つの方法 -
Get-Command
コマンドレットを使用した2つの方法
-
- 自作したFunctionでFunction定義を確認する方法
定義を確認する以外に 改行コードの指定 と Markdownのコードブロック形式で出力 を可能とした。
対応方法
おおきくわけて コマンドレットでFunction定義を確認する方法
と 自作したFunctionでFunction定義を確認する方法
の2つを紹介。
確認対象のFunctionは、こちらの記事で紹介しているファイルのロック状態を確認するコード(Test-FileLocked
)をそれぞれで確認してみます。
対応方法1: コマンドレットで確認する4つの方法
(Get-ChildItem function:Test-FileLocked).Definition
(Get-ChildItem function:Test-FileLocked).ScriptBlock
(Get-Command 'Test-FileLocked').ScriptBlock.ToString()
(Get-Command 'Test-FileLocked').ScriptBlock
一番わかりやすく使用頻度が高い方法は、おそらくC. Get-Commandを使い文字列形式で取得する方法 の「(Get-Command $FunctionName).ScriptBlock.ToString()
」の方法でしょう。
実際にコマンドレットを実行した結果
4つのコマンドそれぞれ同じ結果となるため、Aの結果のみを下記に記載。
# 上記4ついずれもコンソール上の結果は同じのため、Cのコマンド結果のみ表示
PS D:\Downloads> (Get-Command 'Test-FileLocked').ScriptBlock.ToString()
# ここからがコマンドレットの出力結果
param (
[Parameter(Mandatory=$true)][System.String]$Path
)
if (-Not(Test-Path $Path)) {
Write-Error '対象ファイルが存在しません。' -ErrorAction Stop
}
# 相対パスだとOpenメソッドが正常動作しない為、絶対パスに変換
$fullPath = (Resolve-Path -Path $Path -ErrorAction SilentlyContinue).Path
$fileLocked = $false
try {
# 読み取り専用でファイルを開く処理を実行
$fileStream = [System.IO.File]::Open($fullPath, 'Open', 'ReadWrite', 'None')
}
catch {
# ファイルが開けない場合、ロック状態と判断
$fileLocked = $true
}
finally {
if ($null -ne $fileStream) {
$fileStream.Close()
}
}
return $fileLocked
# ここまでがコマンドレットの出力結果
PS D:\Downloads>
それぞれコマンド結果を変数に代入し、想定するデータ型で格納されているかも確認しました。
# A. Get-ChildItemを使い文字列形式で取得する方法
PS D:\Downloads> $result = (Get-ChildItem function:Test-FileLocked).Definition
PS D:\Downloads> $result.GetType().FullName
System.String
PS D:\Downloads>
# B. Get-ChildItemを使いスクリプトブロック形式で取得する方法
PS D:\Downloads> $result = (Get-ChildItem function:Test-FileLocked).ScriptBlock
PS D:\Downloads> $result.GetType().FullName
System.Management.Automation.ScriptBlock
PS D:\Downloads>
# C. Get-Commandを使い文字列形式で取得する方法
PS D:\Downloads> $result = (Get-Command 'Test-FileLocked').ScriptBlock.ToString()
PS D:\Downloads> $result.GetType().FullName
System.String
PS D:\Downloads>
# D. Get-Commandを使いスクリプトブロック形式で取得する方法
PS D:\Downloads> $result = (Get-Command 'Test-FileLocked').ScriptBlock
PS D:\Downloads> $result.GetType().FullName
System.Management.Automation.ScriptBlock
PS D:\Downloads>
4つすべて想定通りのデータ型でした。
補足情報:文字列形式 と スクリプトブロック形式 の違い
それぞれの違いを実際のコードで解説します。
-
文字列形式
文字通りテキストデータを格納する為に使用。
テキストデータだけでは何も処理できない。
代表的な使用方法は、出力系のコマンドレット(Write-Host
など)と組み合わせる方法。文字列を宣言してコマンドで出力# 文字列配列を宣言 $months = @( @("1月", "January", "ˈjæn.ju.er.i (ジャニュエリ)"), @("2月", "February", "ˈfeb.ruː.er.i (フェブルエリ)"), @("3月", "March", "mɑːrtʃ (マーチ)"), @("4月", "April", "ˈeɪ.prəl (エイプリル)"), @("5月", "May", "meɪ (メイ)"), @("6月", "June", "dʒuːn (ジューン)"), @("7月", "July", "dʒʊˈlaɪ (ジュライ)"), @("8月", "August", "ˈɔː.ɡəst (オーガスト)"), @("9月", "September", "sɛpˈtɛmbər (セプテンバー)"), @("10月", "October", "ɒkˈtoʊbər (オクトーバー)"), @("11月", "November", "noʊˈvɛmbər (ノーヴェンバー)"), @("12月", "December", "dɪˈsɛmbər (ディセンバー)") ) # 文字列配列を行ごとに出力 foreach ($row in $months) { Write-Host "$($row[0]), $($row[1]), $($row[2])" } # 実行結果 1月, January, ˈjæn.ju.er.i (ジャニュエリ) 2月, February, ˈfeb.ruː.er.i (フェブルエリ) 3月, March, mɑːrtʃ (マーチ) 4月, April, ˈeɪ.prəl (エイプリル) 5月, May, meɪ (メイ) 6月, June, dʒuːn (ジューン) 7月, July, dʒʊˈlaɪ (ジュライ) 8月, August, ˈɔː.ɡəst (オーガスト) 9月, September, sɛpˈtɛmbər (セプテンバー) 10月, October, ɒkˈtoʊbər (オクトーバー) 11月, November, noʊˈvɛmbər (ノーヴェンバー) 12月, December, dɪˈsɛmbər (ディセンバー)
-
スクリプトブロック形式
実行可能なデータとして定義できる形式。
スクリプトブロックは、中括弧({}
)に囲まれたコマンドや式の集合体でそのまま実行できる。
また、スクリプトブロック型として変数に格納したり、スクリプトブロックを対応する他のコマンドレット(Invoke-Command
)に引き渡し実行が可能。スクリプトブロックで宣言し実行# スクリプトブロックで英語先頭3桁から曜日を検索するコードを宣言 $findDay = { param ( [System.String]$Abbreviation ) # スクリプトブロック内で文字列配列を宣言 $daysOfTheWeek = @( @("日曜", "Sunday", "ˈsʌn.deɪ (サンデイ)"), @("月曜", "Monday", "ˈmʌn.deɪ (マンデイ)"), @("火曜", "Tuesday", "ˈtjuːz.deɪ (チューズデイ)"), @("水曜", "Wednesday", "ˈwɛnz.deɪ (ウェンズデイ)"), @("木曜", "Thursday", "ˈθɜːrz.deɪ (サーズデイ)"), @("金曜", "Friday", "ˈfraɪ.deɪ (フライデイ)"), @("土曜", "Saturday", "ˈsæt.ər.deɪ (サタデイ)") ) # 配列を検索 $dayFound = $daysOfTheWeek | Where-Object { $_[1].Substring(0,3).ToLower() -eq $abbreviation.ToLower() } if ($dayFound) { Write-Host "ヒットしました。日本語の曜日「$($dayFound[0])」" } else { Write-Host "該当する曜日が見つかりませんでした。" } } # 実行結果 # 直接スクリプトブロックを実行 PS D:\Downloads> & $findDay -Abbreviation "Thu" ヒットしました。日本語の曜日「木曜」 PS D:\Downloads> # Invoke-Commandに引き渡して実行 PS D:\Downloads> Invoke-Command -ScriptBlock $findDay -ArgumentList "Sun" ヒットしました。日本語の曜日「日曜」 PS D:\Downloads>
対応方法2: 自作したFunctionで確認する方法
function Get-FunctionDefinition {
param (
[System.String]$FunctionName,
[System.String]$Newline="`r`n",
[System.Boolean]$MarkdownMode=$False
)
# Functionが存在しない場合
if ($null -eq (Get-Command -Name $FunctionName -ErrorAction SilentlyContinue)) {
Write-Warning "該当のFunctionが見つかりませんでした。[検索したFunction名: $($FunctionName)]"
return
}
# 先頭の空行を飛ばしてFunctionの定義内容を取得
# スクリプトブロック内の改行コードを「LF」で設定(※ PowerShellバージョンによって変わるかも)
$scriptblockNewline = "`n"
$plainDefinition = (((Get-Command $FunctionName).ScriptBlock.ToString() -Split $scriptblockNewline | Select-Object -Skip 1) -Join $Newline)
$functionDefinition = ''
if ($MarkdownMode) {
$functionDefinition += "``````powershell$($Newline)"
$functionDefinition += "function $FunctionName {$($Newline)"
$functionDefinition += $plainDefinition
$functionDefinition += "}$($Newline)"
$functionDefinition += "``````$($Newline)"
}
else {
$functionDefinition += "function $FunctionName {$($Newline)"
$functionDefinition += $plainDefinition
$functionDefinition += "}$($Newline)"
}
return $functionDefinition
}
実際に実行した結果
今回、自作したFunctionは3つの引数がありますが、下記のとおり必須の引数である「確認対象のFunction名」のみを指定し実行すると、
前述したコマンドと同じ結果を返します。
PS D:\Downloads> Get-FunctionDefinition Test-FileLocked
# ここからが自作Functionの出力結果
function Test-FileLocked {
param (
[Parameter(Mandatory=$true)][System.String]$Path
)
if (-Not(Test-Path $Path)) {
Write-Error '対象ファイルが存在しません。' -ErrorAction Stop
}
# 相対パスだとOpenメソッドが正常動作しない為、絶対パスに変換
$fullPath = (Resolve-Path -Path $Path -ErrorAction SilentlyContinue).Path
$fileLocked = $false
try {
# 読み取り専用でファイルを開く処理を実行
$fileStream = [System.IO.File]::Open($fullPath, 'Open', 'ReadWrite', 'None')
}
catch {
# ファイルが開けない場合、ロック状態と判断
$fileLocked = $true
}
finally {
if ($null -ne $fileStream) {
$fileStream.Close()
}
}
return $fileLocked
}
# ここまでが自作Functionの出力結果
PS D:\Downloads>
上記のとおりFunction「Test-FileLocked
」の中身が確認できました。
つづいては同じ自作Functionに追加で引数を渡すことで出力結果をカスタマイズしてみます。
PS D:\Downloads> Get-FunctionDefinition -FunctionName "Test-FileLocked" -Newline "`n" -MarkdownMode $True
# ここからが自作Functionの出力結果
```powershell
function Test-FileLocked {
param (
[Parameter(Mandatory=$true)][System.String]$Path
)
if (-Not(Test-Path $Path)) {
Write-Error '対象ファイルが存在しません。' -ErrorAction Stop
}
# 相対パスだとOpenメソッドが正常動作しない為、絶対パスに変換
$fullPath = (Resolve-Path -Path $Path -ErrorAction SilentlyContinue).Path
$fileLocked = $false
try {
# 読み取り専用でファイルを開く処理を実行
$fileStream = [System.IO.File]::Open($fullPath, 'Open', 'ReadWrite', 'None')
}
catch {
# ファイルが開けない場合、ロック状態と判断
$fileLocked = $true
}
finally {
if ($null -ne $fileStream) {
$fileStream.Close()
}
}
return $fileLocked
}
```
# ここまでが自作Functionの出力結果
PS D:\Downloads>
上記だと改行コードは判断できませんが、Markdownのコードブロック形式(```powershell
ではじまり、```
で終わる)で出力されていることが確認できました。
今回は冗長的な内容になってしまうため割愛しますが、改行コードの確認はリダイレクトなどで別ファイルとして出力すると確認できると思います。
まとめ
- PowerShellで定義済みのFunctionの中身を取得する方法を調べた
-
対応方法1: コマンドレットで確認する4つの方法
-
A. Get-ChildItemで文字列形式を取得
(Get-ChildItem function:"対象のFunction名").Definition`
-
B. Get-ChildItemでスクリプトブロック形式を取得
(Get-ChildItem function:"対象のFunction名").ScriptBlock`
-
C. Get-Commandで文字列形式を取得(🌟おそらく、これが一番可読性がよく使用頻度も高い)
(Get-Command "対象のFunction名").ScriptBlock.ToString()`
-
D. Get-Commandでスクリプトブロック形式を取得
(Get-Command "対象のFunction名").ScriptBlock`
-
-
対応方法2: 自作したFunctionで確認する方法
Get-FunctionDefinition -FunctionName "対象のFunctioon名"
-
関連記事
Discussion