🌠

[PowerShell]ワンライナーでコマンドの存在チェックし条件分岐させる方法

2023/10/27に公開

概要

コマンドプロンプトやPowerShellにおいてコマンドの存在チェックしTrue・Falseの真偽で条件分岐する方法をこちらの記事で見つけました。
PowerShellの欄をみると「&&||に対応するものは、おそらくないだろう」という趣旨の記述があり、
深掘りして調べてみた結果、やり方が見つかったので紹介します。

この記事のターゲット

  • PowerShellユーザーの方
  • PowerShellコマンドレットの存在有無により真偽(True・False)で条件分岐させたい方

環境

PowerShellのバージョン
PS C:\Users\XXXX> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.3.8
PSEdition                      Core
GitCommitId                    7.3.8
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

PS C:\Users\XXXX>

見つかった記事の内容

前述した通り、こちらでShellScript(シェルスクリプト)やコマンドプロンプトを始めとする環境で、
コマンドの存在チェックを行い存在有無の結果、True・Falseの真偽で処理を分岐させる方法が紹介されていました。

その内、PowerShellの箇所で以下のように紹介されていました。

# PowerShell

### ワンライナー
&&||に対応するものはたぶんない ?

### 複数行

Get-Command (gcm) コマンドレットを使う.

gcm perldoc -ea SilentlyContinue | Out-Null
if ($? -eq $true) { # コマンドが存在すれば
    Write-Output 'Success!'
    perldoc perldoc
} else {            # コマンドが存在しなければ
    Write-Error 'Error!'
    exit 1
}

簡潔に書くと:

if (gcm perldoc -ea SilentlyContinue) { # コマンドが存在すれば
    Write-Output 'Success!'
    perldoc perldoc
} else {                                # コマンドが存在しなければ
    Write-Error 'Error!'
    exit 1
}

引用元:コマンドが存在するかしないかを判定して, 条件分岐する構文のまとめ - PowerShell

複数行でも実現できる為、あえてワンライナーでコーディングする必要はありませんが、
本当にワンライナーで実現する方法がないのか気になったので深掘りして調べてみました。
(個人的には、可読性を考えるとワンライナーよりも複数行が良いと思っています。)

対応方法

こちらの記事にある見出し「論理演算」の欄で「 &&-and 」「 ||-or 」と紹介されていたので、
-and 」を使ってワンライナーのコマンドを作ってみました。

PowerShellのワンライナーコマンド

コピー用(PowerShellのワンライナーコマンド)

コマンドが存在する場合に「 Success! 」を表示
(Get-Command -Name "対象コマンド名" -ErrorAction SilentlyContinue) -and (Write-Output "存在した場合のメッセージ") | Out-Null
コマンドが存在しない場合に「 Error! 」を表示
-Not(Get-Command -Name "対象コマンド名" -ErrorAction SilentlyContinue) -and (Write-Error "存在しない場合のメッセージ") | Out-Null

上記で実現できましたが、他の言語やスクリプトなどと比較すると違和感のあるワンライナーになってしまいました。

以下がこれらのコマンドを実際に実行した結果です。

実際の実行結果

実際に実行した結果
# Test-Path が存在するコマンドである為、「 Success! 」と表示
PS C:\Users\XXXX> (Get-Command -Name Test-Path -ErrorAction SilentlyContinue) -and (Write-Output 'Success!') | Out-Null
Success!
PS C:\Users\XXXX>
# perldoc が存在しないコマンドである為、「 Error! 」と表示
PS C:\Users\XXXX> -Not(Get-Command -Name perldoc -ErrorAction SilentlyContinue) -and (Write-Error 'Error!') | Out-Null
Error!
PS C:\Users\XXXX>

ちまみにGet-Command -Name "対象コマンド名" -ErrorAction SilentlyContinueにパイプライン(|)をつけた後、Where-Object(省略だと?{})というコマンド構成でどうにかできないか、
いろいろ試してみましたが、うまくいく方法を見つける事ができませんでした。

他にもっと良いやり方があるかもしれません。

セミコロン(;)を使った方法

あと、ワンライナーの定義で複数コマンド(複文?)の構成がNG条件だとワンライナーに当たりませんが、
下記のやり方でも動作します。
(セミコロン ; でコマンド同士を区切る事で複数行のコマンドを1行で実現可能 )

複数コマンドを1行で構成したパターン
# コマンドレットやオプションを省略
# 「Get-Command   → gcm」
# 「-ErrorAction  → -ea」
gcm Test-Path -ea SilentlyContinue | Out-Null ; if ($?) {Write-Output 'Success!'}
gcm perldoc -ea SilentlyContinue | Out-Null ; if (-Not($?)) {Write-Error 'Error!'}
コピー用[セミコロン(;)を使った方法]
コマンドが存在する場合に「 Success! 」を表示
gcm "対象コマンド名" -ea SilentlyContinue | Out-Null ; if ($?) {Write-Output "存在した場合のメッセージ"}
コマンドが存在しない場合に「 Error! 」を表示
gcm "対象コマンド名" -ea SilentlyContinue | Out-Null ; if (-Not($?)) {Write-Error "存在しない場合のメッセージ"}

正直、前述した論理演算の -and を使う方法より、こちらのセミコロン;で区切る方法の方がわかりやすいと感じます。

補足事項:Get-Command と Find-Command の違いをBing AIに聞いてみた

脱線しますが、PowerShellのコマンドを調べる方法がGet-Command以外にもFind-Commandというコマンドレットもありました。
公式のリファレンスを確認しましたが、いまいち違いがわからなかったので、Bing AI(Microsoft Copilot・Bing チャット)に聞いてみました。

Bing AIの回答内容 < クリックで折りたたみが開く >

❓ Bing AIへの依頼内容("より創造的に")

PowerShellにおけるコマンドレット「Get-Command」と「Find-Command」の違いを教えて


✅ Bing AIからの回答

こんにちは、これはBingです。PowerShellに関する質問をありがとうございます。😊

コマンドレット「Get-Command」と「Find-Command」の違いは次のとおりです。

  • 「Get-Command」は、コンピューターにインストールされているすべてのコマンドを取得します。コマンドの種類や名前、動詞、名詞などで絞り込むことができます¹。
  • 「Find-Command」は、登録済みのリポジトリ内のモジュールを検索し、その中に含まれるコマンドを取得します。モジュールの名前やバージョン、タグなどで絞り込むことができます²。

たとえば、「Get-Command -Verb Get」は、動詞が「Get」であるすべてのコマンドを表示します。一方、「Find-Command -Verb Get」は、リポジトリ内のモジュールに含まれる動詞が「Get」であるコマンドを表示します。

「Get-Command」は、モジュールを自動的にインポートすることができますが、「Find-Command」はインポートしません。インポートするには、「Find-Command」の結果を「Install-Module」にパイプする必要があります²。

以上が、「Get-Command」と「Find-Command」の違いについての説明です。参考になれば幸いです。👍

¹: Get-Command (Microsoft.PowerShell.Core) - PowerShell
²: Find-Command (PowerShellGet) - PowerShell | Microsoft Learn

ソース: Bing との会話 2023/10/26
(1) Find-Command (PowerShellGet) - PowerShell | Microsoft Learn. https://learn.microsoft.com/ja-jp/powershell/module/powershellget/find-command?view=powershellget-2.x.
(2) Get-Command (Microsoft.PowerShell.Core) - PowerShell. https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/get-command?view=powershell-7.3.
(3) Get-Command (Microsoft.PowerShell.Core) - PowerShell. https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/get-command?view=powershell-7.3.
(4) Find-Command (PowerShellGet) - PowerShell | Microsoft Learn. https://learn.microsoft.com/ja-jp/powershell/module/powershellget/find-command?view=powershellget-2.x.
(5) Powershellで分からないコマンドはPowershellで調べる - Qiita. https://qiita.com/Anubis_369/items/9e265f479187c40cc626.
(6) コマンドレットの概要 - PowerShell | Microsoft Learn. https://learn.microsoft.com/ja-jp/powershell/scripting/developer/cmdlet/cmdlet-overview?view=powershell-7.2.
(7) 【PowerShell】コマンド入門編(Get-Command) - なんだか .... https://nandakagoodvibes.hatenablog.com/entry/2021/07/18/164927.


Bing AIの回答によると、

  • Get-Command
    すべてのコマンドリストから取得できるコマンド
  • Find-Command
    登録済みのリポジトリ内のモジュール検索で取得できるコマンド

という内容でした。

Find-Commandの公式リファレンスをみるとPowerShellのバージョンが 2.x までの文書しかないようです。
とくに、こだわりが無ければGet-Commandの方を使用した方が無難そうという事は何となくわかりました。

さらに補足情報:PowerShellにおける登録済みリポジトリ内のモジュールとは < クリックで折りたたみが開く >

❓ Bing AIへの依頼内容("より創造的に")

PowerShellにおける登録済みリポジトリ内のモジュールとは


✅ Bing AIからの回答

PowerShellにおける登録済みリポジトリ内のモジュールとは、PowerShellGetというモジュール管理システムで利用できるモジュールの集合です¹。登録済みリポジトリとは、モジュールを検索したりインストールしたりするためのソースの場所を指定したものです²。登録済みリポジトリは、PowerShellギャラリー³やNuGet⁴などのオンラインサービスや、ローカルフォルダやネットワーク共有などのオフラインソースに設定できます²。登録済みリポジトリを管理するには、Set-PSRepository⁵やFind-PSRepository⁶などのコマンドレットを使用します²。

以上が、PowerShellにおける登録済みリポジトリ内のモジュールについての説明です。参考になれば幸いです。👍

¹: PowerShellGet とは - PowerShell | Microsoft Learn
²: Set-PSRepository (PowerShellGet) - PowerShell | Microsoft Learn
³: [PowerShell Gallery | Home]
⁴: [NuGet Gallery | Home]
⁵: [Set-PSRepository (PowerShellGet) - PowerShell | Microsoft Learn]
⁶: [Find-PSRepository (PowerShellGet) - PowerShell | Microsoft Learn]

ソース: Bing との会話 2023/10/26
(1) Set-PSRepository (PowerShellGet) - PowerShell | Microsoft Learn. https://learn.microsoft.com/ja-jp/powershell/module/powershellget/set-psrepository?view=powershellget-2.x.
(2) PowerShellにおけるモジュールの取扱い方 #PowerShell - Qiita. https://qiita.com/tomomoss/items/5f8c027f3bdc3b189791v
(3) モジュールのインポート方法 - Forsenergy. https://forsenergy.com/ja-jp/windowspowershellhelp/html/a21640bb-853f-4dc2-b43a-fb44d3a774b2.htm.
(4) undefined. https://someNuGetUrl.com/api/v2.
(5) undefined. https://someNuGetUrl.com/api/v2/packages.
(6) undefined. https://someNuGetUrl.com/api/v2/Packages.
(7) Set-PSRepository (PowerShellGet) - PowerShell | Microsoft Learn. https://learn.microsoft.com/ja-jp/powershell/module/powershellget/set-psrepository?view=powershellget-2.x.
(8) PowerShellにおけるモジュールの取扱い方 #PowerShell - Qiita. https://qiita.com/tomomoss/items/5f8c027f3bdc3b189791.


実際、私の環境における登録済みリポジトリ内のモジュールGet-PSRepositoryコマンドで調べてみました。

登録済みリポジトリ内のモジュールを調べた
PS C:\Users\XXXX> Get-PSRepository

Name                      InstallationPolicy   SourceLocation
----                      ------------------   --------------
PSGallery                 Untrusted            https://www.powershellgallery.com/api/v2

PS C:\Users\XXXX>

私の環境下で登録済みリポジトリ内のモジュールは「 PSGallery 」のみのようです。
これらの環境は触っていないので、おそらくこのコマンド結果は、初期設定の状態だと思われます。

参考情報

まとめ

  • PowerShellでワンライナーでコマンドの存在チェックし条件分岐させる方法
    • 純粋(?)なワンライナーの方法

      コマンドが存在する場合
      (Get-Command -Name "対象コマンド名" -ErrorAction SilentlyContinue) -and (Write-Output "存在した場合のメッセージ") | Out-Null`
      
      コマンドが存在しない場合
      -Not(Get-Command -Name "対象コマンド名" -ErrorAction SilentlyContinue) -and (Write-Error "存在しない場合のメッセージ") | Out-Null
      
    • 複数コマンドで実行する方法
      コマンドレットやオプションの記述を以下の通り省略。
      「Get-Command → gcm」
      「-ErrorAction → -ea」

      コマンドが存在する場合
      gcm "対象コマンド名" -ea SilentlyContinue | Out-Null ; if ($?) {Write-Output "存在した場合のメッセージ"}
      
      コマンドが存在しない場合
      gcm "対象コマンド名" -ea SilentlyContinue | Out-Null ; if (-Not($?)) {Write-Error "存在しない場合のメッセージ"}
      

関連記事

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

GitHubで編集を提案

Discussion