なぜstartコマンドはブラウザ名だけでブラウザを開けるのか

# はじめに

皆さんはこのコマンドを見たことがありますか？

```bat
start chrome "https://qiita.com/"
```

もし初めて見たという方はコマンドプロンプト（PowerShellはNG）で実行してみてください。Chromeが起動してQiitaのページが表示されるはずです。
※Chromeをインストールしてない場合は代わりに `msedge` や `firefox` を指定してみてください

さて、このコマンドを見て不思議に思いませんか？

**なぜ `C:\Program Files (x86)\Google\Chrome\Application\chrome.exe` のようにフルパスを指定していないのにブラウザが起動するのか？**

この記事ではその理由について解説してみます。

# startコマンド

## startコマンドとは

Microsoftのドキュメントによると、startコマンドは指定したプログラムまたはコマンドを実行するコマンドで、Windowsのコマンドプロンプトの組み込みコマンドのようです。

https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/start

例えば

```bat
start notepad
```

を実行するとメモ帳を起動できますし、

例えば

```bat
start "現在日付" echo %date% %time%
```

を実行するとウィンドウ名が "現在日付" の新しいターミナルを開いて現在日付を表示させられます。

ちなみに、組み込みコマンドだからなのか、PowerShellからは直接実行できないです。
※PowerShellの `start` は `Start-Process` コマンドレットのエイリアス

```powershell
Get-Command start
# CommandType    Name                         Version    Source
# -----------    ----                         -------    ------
# Alias          start -> Start-Process
```

## 実行ファイルの検索

上記の例だとメモ帳を `notepad` だけで指定していますが、メモ帳の実体は `C:\WINDOWS\system32\notepad.exe` です。

ではなぜ前述のChromeの例や上記のメモ帳の例ではファイル名だけでアプリを起動できるのでしょうか？

まず拡張子の省略について、Microsoftのドキュメントに以下のような記載があります。

> 実行するコマンドで最初に使用されているトークンが、コマンドでも、拡張子を持つ既存のファイルへのファイル パスでもない場合は、Cmd.exe により、PATHEXT 環境変数の値を使用して、検索する拡張子とその順序が決定されます。 PATHEXT 変数の既定値は、.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC です。

つまり拡張子が省略された場合は `.com` や `.exe` とみなして検索されるみたいです。

次にディレクトリの省略について、Stackoverflowにまさにちょうど同じ内容の質問がありました。

https://stackoverflow.com/questions/22697138/start-exe-files-from-command-line-windows-from-other-directories

ざっくり要約すると

> Q. firefox.exeをどこでもstartで実行できるのはなんで？他の実行ファイルは親ディレクトリに移動しないと実行できないのに。
>
> A. それは "App Paths" に追加されてるからだよ。これは環境変数を書き換えるよりも優れた方法なんだ。

という内容です。

ここで言及されている "App Paths" というのはレジストリ値で、以下に詳細が書かれています。

https://learn.microsoft.com/ja-jp/windows/win32/shell/app-registration

使い方としては "App Paths" 配下に実行ファイルのファイル名をサブキーとしたエントリを作成して、既定値に実行ファイルのフルパスを、Pathに親ディレクトリを設定するようです。

```txt
\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe

Property
  (default) = C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
  Path      = C:\Program Files (x86)\Google\Chrome\Application
```

気になる方は以下のコマンドをPowerShellで実行してみてください。現時点でどんな値が設定されているかを調べることができます。

```powershell:App Pathsの一覧確認
@("HKCU", "HKLM") | % {
  $rootKey = $_
  Write-Host "${rootKey}"
  $appPaths = (Get-Item "${rootKey}:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths")
  $appPaths.GetSubKeyNames() | % {
    $subKeyName = $_
    Write-Host "  ${subKeyName}"
    $entry = (Get-Item "${rootKey}:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\${subKeyName}")
    $entry.GetValueNames() | % {
      $valueName = $_
      $value = $entry.GetValue($valueName)
      if ($valueName -eq "") {
        $key = "(default)"
      } else {
        $key = $valueName
      }
      Write-Host "    ${key} : ${value}"
    }
  }
}
```

<details><summary>出力結果のサンプル</summary>

```txt
HKCU
  OneDriveFileLauncher.exe
    SkipCloudDownload : 1
  WindowsPackageManagerServer.exe
    (default) : C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.21.3421.0_x64__8wekyb3d8bbwe\WindowsPackageManagerServer.exe
    Path : C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.21.3421.0_x64__8wekyb3d8bbwe
  winget.exe
    (default) : C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.21.3421.0_x64__8wekyb3d8bbwe\winget.exe
    Path : C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.21.3421.0_x64__8wekyb3d8bbwe
  wt.exe
    (default) : C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.18.2822.0_x64__8wekyb3d8bbwe\wt.exe
    Path : C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.18.2822.0_x64__8wekyb3d8bbwe
HKLM
  7zFM.exe
    (default) : C:\Program Files\7-Zip\7zFM.exe
    Path : C:\Program Files\7-Zip\

    :
    :

  chrome.exe
    (default) : C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
    Path : C:\Program Files (x86)\Google\Chrome\Application

    :
    :

  firefox.exe
    (default) : C:\Program Files\Mozilla Firefox\firefox.exe
    Path : C:\Program Files\Mozilla Firefox

    :
    :
```

</details>

startコマンドは環境変数の "Path" に設定されたディレクトリと "App Paths" に設定されたファイル一覧から実行ファイル検索するようです。

なのでChromeの場合は "App Paths" に `chrome.exe` が設定されているので見つけることができ、メモ帳の場合は `C:\WINDOWS\system32` が環境変数の "Path" に設定されているので見つけることができたというわけです。

startコマンドについては以上ですが、以下、関連する話もいくつかまとめてみました。

# 余談: 一時的な環境変数とエイリアス

各ブラウザは "App Paths" に値を設定していたのでパスを省略できていましたが、当然、環境変数に実行ファイルの親ディレクトリを追加することでもパスを省略できるようになります。

また、PowerShellにおいてはプロファイルを用いて一時的に環境変数に追記する、あるいはエイリアスを設定するという方法もあります。

```powershell
# 方法1: 一時的に環境変数にパスを追加する
$env:Path += ";C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

# 方法2: エイリアスを設定する
Set-Alias chrome "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
```

この方法のメリットとして、例えば Excel や PowerPoint のようにコマンドラインオプションが存在するが環境変数にも "App Paths" にも設定されていないアプリを名前だけで呼び出すのに便利です。

```powershell:Officeアプリのエイリアス追加
Set-Alias excel "C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE"
Set-Alias powerpnt "C:\Program Files\Microsoft Office\root\Office16\POWERPNT.EXE"
Set-Alias winword "C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE"
```

コマンドプロンプトでもsetコマンド・doskeyコマンドで一時的な環境変数の変更やエイリアスの設定が可能です。

```bat
@rem 方法1: 一時的に環境変数にパスを追加する
set path=%path$;C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

@rem 方法2: エイリアスを設定する
doskey chrome="C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
```

# 余談: Chromeの起動オプション

そもそもなぜChromeをコマンドラインで実行したかったかというと、Chromeに起動オプションを指定して起動させたかったという背景があります。

Chromeの起動オプションについては以下が詳しいです。

http://chrome.half-moon.org/43.html

自分がよく使うのは `--incognito` と `--app` の2つで、特に `--app` に関しては任意のサイトやローカルファイルをPWAのようにアドレスバーのないウィンドウで表示する目的でよく使っています。

```powershell
# シークレットモードで開く
chrome "https://qiita.com/" --incognito

# アプリケーションモードで開く
chrome --app="https://qiita.com/"

# ブラウザをシンプルなPDFビューアとして起動する
chrome --app="file:///C:/Users/tacos/Desktop/sample.pdf" --incognito
```

この使い方についてはいずれ記事にしたいと思っています。

あと、同じChrome系のEdgeも上記のオプションを使うことができます。Safari/Firefoxはごめんなさい、知りません。

# 余談: PowerShellからの呼び出し

[startコマンドとは](#startコマンドとは) の節でstartコマンドはPowerShellからは直接実行できないといいましたが、間接的になら呼び出せます。

cmd.exeの `/c` パラメータでコマンド文を渡して実行させることができるので、この機能を使います。

```powershell
cmd /c "start chrome --app=`"https://qiita.com/`""
```

この方法であれば、仮にバージョンアップで `C:\Program Files (x86)\Google\Chrome\Application` というパスが別物に変わってしまっても関係なくブラウザを起動させることができます。

# さいごに

startコマンドがブラウザ名、ひいてはアプリ名だけで実行ファイルを実行できる理由について解説してみました。

自分は今までユーザ環境変数のPathやPowerShellプロファイルでブラウザのパスを通すようにしていたんですけど、これがあるならもうパスを通す必要ないかもですね。

ただ、Windowsの組み込みコマンドや "App Paths" が廃止になってしまう可能性もゼロではないので、そこは気をつけたいところです。

記事は以上になります。最後まで読んでくださりありがとうございます。