PowerShell

PowerShell で which コマンドを作る

msys2 等でインストールしたり where.exe を使う方法もありますが、PowerShell だけでできる方法にします。

こちらの記事を参考にしました。

ファイルのパスを取得する方法

Get-Command で返ってくる Definition あるいは Source プロパティにファイルのパスが含まれています。
ですが、 Source プロパティよりも Definition プロパティの方が汎用性が高いので、こちらを使います。

Get-member をしてみると、

PS> gcm pwsh | gm

   TypeName: System.Management.Automation.ApplicationInfo

Name               MemberType     Definition
----               ----------     ----------
Equals             Method         bool Equals(System.Object obj)
GetHashCode        Method         int GetHashCode()
GetType            Method         type GetType()
ResolveParameter   Method         System.Management.Automation.ParameterMetadata ResolveParameter(string name)
ToString           Method         string ToString()
CommandType        Property       System.Management.Automation.CommandTypes CommandType {get;}
Definition         Property       string Definition {get;}
Extension          Property       string Extension {get;}
Module             Property       psmoduleinfo Module {get;}
ModuleName         Property       string ModuleName {get;}
Name               Property       string Name {get;}
OutputType         Property       System.Collections.ObjectModel.ReadOnlyCollection[System.Management.Automation.PST... Parameters         Property       System.Collections.Generic.Dictionary[string,System.Management.Automation.Paramete... ParameterSets      Property       System.Collections.ObjectModel.ReadOnlyCollection[System.Management.Automation.Com... Path               Property       string Path {get;}
RemotingCapability Property       System.Management.Automation.RemotingCapability RemotingCapability {get;}
Source             Property       string Source {get;}
Version            Property       version Version {get;}
Visibility         Property       System.Management.Automation.SessionStateEntryVisibility Visibility {get;set;}
FileVersionInfo    ScriptProperty System.Object FileVersionInfo {get=[System.Diagnostics.FileVersionInfo]::getversio... HelpUri            ScriptProperty System.Object HelpUri {get=$oldProgressPreference = $ProgressPreference...

これだけのものが出てきますが、欲しい情報は Definition だけです。
それだけを取り出す方法はいくつかあり、上の記事で紹介されている (gcm $command).Definition 以外にも

PS > (gcm pwsh).Definition
C:\Program Files\PowerShell\6.0.0\pwsh.exe
PS> gcm pwsh | fl -p Definition


Definition : C:\Program Files\PowerShell\6.0.0\pwsh.exe


PS> gcm pwsh | select -p Definition

Definition
----------
C:\Program Files\PowerShell\6.0.0\pwsh.exe

などがあります。
それぞれ表示が違うので好みで使い分けてください。
上の記事で紹介されていた Format-List もオプションで -Property Definition と指定すれば Definition だけを取り出せます。

コマンドとして使える様にする

意味合い的には Select-Object -Property Definition が一番妥当だと思うのですが、 Select-Object では表示されず Format-List では表示されるものもあるので、 Format-List を使ってコマンド化します。
といっても簡単で、

function which ($command) {
    Get-command -Name $command -ShowCommandInfo | Format-List -Property Definition
}

これで完了です。

これを毎回使える様にするために、このファンクションを $PROFILE に書きましょう。

$PROFILE には4種類あり、

PS> $PROFILE | gm -m NoteProperty | select -p Name, MemberType

Name                     MemberType
----                     ----------
AllUsersAllHosts       NoteProperty
AllUsersCurrentHost    NoteProperty
CurrentUserAllHosts    NoteProperty
CurrentUserCurrentHost NoteProperty

AllUsersCurrentUser かはその名の通り、 AllHostsCurrentHost かはちゃんと分かっていませんが、
PowerShell で CurrentHost に設定した場合 PowerShell ISE には適用されません。
個人で使っている範囲で、PowerShell にも ISE にも適用して欲しいのであれば、 CurrentUserAllHosts に書くのが良いでしょう。

# ディレクトリやファイルが無い場合、ファイルを作成
PS> $profilePath = Split-Path -Path $PROFILE.CurrentUserAllHosts -Parent
PS> $profileName = Split-Path -Path $PROFILE.CurrentUserAllHosts -Leaf
PS> mkdir $profilePath
PS> ni -Path $profilePath -Name $profileName
# 既定のエディタで開く
PS> ii $PROFILE.CurrentUserAllHosts
# ファンクションを書いて保存したら読み込む
PS> . $PROFILE.CurrentUserAllHosts

これで PowerShell で which コマンドのようなものが使える様になりました。

おまけ

  1. 上で

    Source プロパティよりも Definition プロパティの方が汎用性が高い

    と言ったのは pwsh(.exe) のようにファイルのものではどちらでも変わりませんが、例えばファンクションやエイリアス、コマンドレットでは

    PS > gcm which | fl -Property Source
    
    Source :
    
    PS > gcm gcm | fl -Property Source
    
    Source :
    
    PS > gcm which | fl -Property Definition
    
    Definition : param($command)
    
                 Get-Command -Name $command -ShowCommandInfo | Format-List -Property Definition
    
    PS > gcm gcm | fl -Property Definition
    
    Definition : Get-Command
    
    PS > gcm Get-Command | fl -Property Definition
    
    Definition :
                 Get-Command [[-ArgumentList] <Object[]>] [-Verb <string[]>] [-Noun <string[]>] [-Module <string[]>] [-FullyQualifiedModule <ModuleSpecification[]>] [-TotalCount <int>] [-Syntax] [-ShowCommandInfo] [-All] [-ListImported] [-ParameterName <string[]>] [-ParameterType <PSTypeName[]>] [<CommonParameters>]
    
                 Get-Command [[-Name] <string[]>] [[-ArgumentList] <Object[]>] [-Module <string[]>] [-FullyQualifiedModule<ModuleSpecification[]>] [-CommandType <CommandTypes>] [-TotalCount <int>] [-Syntax] [-ShowCommandInfo] [-All] [-ListImported] [-ParameterName <string[]>] [-ParameterType <PSTypeName[]>] [<CommonParameters>]
    
    

    と違いが出ます。

    このように which コマンドを定義しておけば、ファイルのパスを見つけたい時だけではなく、ファンクションの定義を確認したいとき例えば which mkdir やコマンドレットのオプションを確認したいとき Get-Help Get-Command の代わりに which Get-Command と使える様になります。

  2. 上で

    Select-Object では表示されず Format-List では表示されるものもある

    と書いた事について

    これも1つめと同じでファンクションやコマンドレットで差が出ます。 Definition の出力が複数行にわたる場合や長い場合 Select-Object では省略されます。

    PS > gcm which | select -p Definition
    
    Definition
    ----------
    param($command)...
    
    PS> gcm Get-Command | select -p Definition
    
    Definition
    ----------
    ...