コンテンツにスキップ
メインメニュー
メインメニュー
サイドバーに移動
非表示
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
Turgenev's Wiki
検索
検索
表示
ログイン
個人用ツール
ログイン
Windowsでエクスプローラーからフォルダを各種ターミナルで開くのソースを表示
ページ
議論
日本語
閲覧
ソースを閲覧
履歴を表示
ツール
ツール
サイドバーに移動
非表示
操作
閲覧
ソースを閲覧
履歴を表示
全般
リンク元
関連ページの更新状況
特別ページ
ページ情報
表示
サイドバーに移動
非表示
←
Windowsでエクスプローラーからフォルダを各種ターミナルで開く
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、以下のグループに属する利用者のみが実行できます:
登録利用者
。
このページのソースの閲覧やコピーができます。
Explorerの右クリックメニューやアドレスバーからフォルダを各種ターミナルで開くための設定方法の紹介。cmd.exe, powershell(pwsh含む), Cygwin, Git Bashの4つに対応。 == 基本 == * [[Windowsの右クリックメニューから複数ファイルをまとめて開く|Windowsの右クリックメニューから複数ファイルをまとめて開く]] のように、レジストリでフォルダ・フォルダ背景の右クリックメニューを編集できる。 ** Windows10までの右クリックメニューに戻そう。 ** <code>command</code>キーの既定値に直接コマンドを書く方法と、<code>DelegateExecute</code>値(など)を使ってCOMを通じて実行する方法(このために[https://github.com/ge9/ExecuteCommand-Pipe https://github.com/ge9/ExecuteCommand-Pipe](使い方はレポジトリの説明を参照)を作った)がある。 ** HKLMにあるマシン全体の設定は、それ自体は管理者権限(あるいはTrustedInstallerなどさらに上の権限)がないと書き換えられないが、<strong>HKCUに値があればそちらが優先される</strong>ので、自分だけに関する設定変更ならユーザー権限だけで可能である。 ** <code>Directory</code>や<code>Directory\Background</code>の直下に<code>runas</code>という名前でキーを作った場合は、自動的に<code>command</code>の内容が管理者権限で実行される。ただ、<code>runas</code>という固定のただ一つの名前しか使えないため、例えば管理者としてコマンドプロンプトを開くメニューとPowerShellを開くメニューを共存させることができない。そのためこの記事では<code>runas</code>キーを使用するのではなくコマンドの中で明示的に管理者としての実行を行っている。 * Explorerのアドレスバー(address bar, location barなど)にcommandと入力してEnterを押すと、そのフォルダをカレントディレクトリとして<code>command</code>というコマンドが実行される。 ** このアドレスバーの仕様はおそらく「ファイル名を指定して実行」とほぼ同じ。<code>command</code>で(メインとして)指定するファイルはフルパスなら必ず動作し、Pathが通っているまたはApp Pathsに登録されているならファイル名だけでよく、さらにbatやexeの場合は拡張子も省略できる。さらに、ほとんど知られていないが実は<strong>App Pathsの値には実行ファイル以外も書ける</strong>ようである。 ** つまり、パスの通っているフォルダにあるimage.pngはフルパスを打たなくても「image.png」で開けるし、さらにはApp Pathsの<code>openimage.exe</code>というキーにimage.pngのフルパスを書けば<code>openimage</code>と打つだけで(実際にはそのような実行ファイルは全く存在しないにもかかわらず)image.pngが開くようになる。当然、vbsなども拡張子無しで呼び出せることになる。 == ポイント == ==== パス名のルール ==== * Windowsではパスの長さが260文字以下という制限がある。詳しくは[[Windowsのパス長さ制限に関して|Windowsのパス長さ制限に関して]] 参照。 ** 制限を超えるような長いパスは<code>\\?\</code>というプレフィックスを付けて(UNCパスの一種として)扱われる。 *** このようなフォルダをカレントディレクトリとしてコマンドを実行することはできないので、アドレスバーに打ち込む方式は使えない(System32をカレントディレクトリとして実行される)。wt -dなどにも指定できない。 *** 右クリックメニューでは、commandの既定の値に直接書く方式の場合は、<code>NoWorkingDirectory</code>を設定しないと呼び出されなくなる。 *** シンボリックリンク/ジャンクションでパスを短くしてやれば普通に扱えるようになる * 普通に入力できる文字でWindowsのファイル名に使えないのは、<code>\</code> <code>/</code> <code>:</code> <code>*</code> <code>?</code> <code>"</code> <code><</code> <code>></code> <code>|</code>の9個(エクスプローラーの画面で入れようとすると表示される)。 ** その他、ヌル文字やタブスペースのような0x20未満の制御文字も使えない(はず)。 *** これらを含むファイルはGit Bash/Cygwinを使っても作れない。 *** NTFS自体の制限はおそらく<code>/</code>だけで、それ以外に関してはLinux上で作ってからWindowsで見ると見られることもある。中身の閲覧などは不可。 * また、末尾が空白またはピリオドのファイルも禁止で、Explorerやcmdでは作れない(自動で除去される)。 ** これらはGit Bash/Cygwinなら作れる。前者はエクスプローラーで閲覧など多少の操作ができるが後者はほとんど何もできず、DelegateExecuteの右クリックメニューでもピリオドが外れた名前しか取得できない。 * 先頭が空白のファイルはExplorerだと作れないがcmdなら作れる。禁止ではないはず。 <span id="git-bashcygwin"></span> ==== Git Bash/Cygwin ==== * Git BashではWindowsの実行ファイルにスラッシュ付きオプションを渡すときはスラッシュを2つにする必要がある。<code>cmd //c</code>や<code>bcdedit //v</code>など。 * printf関数の%qオプションを使うと、文字列を<strong>エスケープされた状態にして</strong>返してくれる。これはshからの呼び出し先の別のshで再びフォルダ名をcdなどの引数として利用したいときに使える。 ** bash(POSIX互換ではない)の組み込みコマンドのprintfと、独立実行ファイル(GNU Coreutilsの一部)である/bin/printfがある。基本的な趣旨としては同一のコマンドだが、組み込みコマンドのほうは入力に<code>"</code>が含まれていない限り結果にも<code>"</code>が使われることはないようで、このおかげで意図しない動作を回避できることがある。今回扱う例に関しては前者が後者の上位互換であるということになる。後者を使っているものはそれでも動いたものである。 ** そもそも%qは組み込みコマンドのほうにしかないという話もある。環境によって違いそう。 * <code>\\?\</code>が付くような長いパスをカレントディレクトリにしていても、Git Bash/Cygwinに付属の(というかMSYS/Cygwin向けにコンパイルされた?)exeなら実行できる。 * Git Bashの(ba)shは<code>--login</code>をつけて起動してもカレントディレクトリが維持されるが、<s>Cygwinの(ba)shは</s><s><code>--login</code></s><s>をつけると強制的にホームディレクトリに移動する。</s> ** 2024/4頃、Cygwinの挙動が変化し、カレントディレクトリが維持されるようになっていることに気付く。変わっていた方のバージョンは3.5.3、変わっていない方は3.3.6であった。 ** ところで、[https://superuser.com/questions/345964/start-bash-shell-cygwin-with-correct-path-without-changing-directory Start bash shell (cygwin) with correct path without changing directory - Super User]の通り、<code>CHERE_INVOKING</code>という変数を1に設定しておけばカレントディレクトリは維持される。 * Git Bashにおいては環境変数を正しく設定するため<code>--login</code>の時点で環境変数<code>MSYSTEM</code>を<code>MINGW64</code>にしておく必要がある。[[Windowsでのターミナル環境|Windowsでのターミナル環境]] 参照。 <span id="powershellpwsh"></span> ==== powershell/pwsh ==== * バージョン5まで(Windows PowerShell)はpowershell.exe、6以降(PowerShell Core, PowerShell)はpwsh.exeと実行ファイル名が違う ** 以後、「powershell(.exe)」と「pwsh(.exe)」で呼び分ける。ただし手元ではpowershellの5.1とpwshの7.2~7.3くらいでしか試していないので、一部バージョンではまた違う可能性もある。 ** pwshは必ずしもWindowsに標準では入っていないので、以下の例でもあまり依存しないように気を付ける。 ** 基本、シングルクォートで囲えばほとんどの特殊文字が無効になり、シングルクォート自体を書くときは2つ並べる。ただし注意点として、驚くべきことにpowershell/pwshでは「<strong>全角のシングルクォート</strong>(U+2018-U+201B、<code>‘</code> <code>’</code> <code>‚</code> <code>‛</code>の4つ)」も通常のシングルクォートと同じ効力があるので、同じようにエスケープする必要がある(参照: [https://learn.microsoft.com/ja-jp/powershell/scripting/lang-spec/chapter-02?view=powershell-7.2 https://learn.microsoft.com/ja-jp/powershell/scripting/lang-spec/chapter-02?view=powershell-7.2])。掲載したスクリプトでは省略しているがやることは同じ。 * powershell.exe ** -Commandがなくても付けたのと同じ扱いになる? *** 逆に、-Commandを付けなければいけないという点を除いてはpwshがpowershellの上位互換という感覚である(新しいのでそれはそう)。 ** <code>`</code>や<code>[]</code>を含むフォルダの扱いにバグがあり、cmdなどでこれらのフォルダをカレントディレクトリとした状態で5系のPowerShellを起動すると<code>C:\</code>や<code>C:\Windows\System32\WindowsPowerShell\v1.0></code>をカレントディレクトリとして起動される(たとえば<code>C:\somefolder][</code>なら前者、<code>C:\somefolder[]</code>なら後者)。 ** -Fileを使用してスクリプトを実行するとき、ダブルクォーテーションで囲われていない <code>-</code> が引数に含まれているとエラーになるバグがある。 * powershell.exe・pwsh.exe共通 ** 連続した<code>`</code>があるフォルダに関する挙動にバグがある。コンソールプログラムを実行しても新規ウインドウで開かれる、そこをカレントディレクトリとしたときにファイルをmvできないなど。 ** <code>`</code>や<code>[]</code>を含むフォルダに関しても、絶対パス指定したりきちんとエスケープしたりすれば操作自体はできる(場合もある)。 *** エスケープはいずれも直前に<code>`</code>を付ける。 *** これらを特殊文字として扱いたくないときは<code>-LiteralPath</code>を付けると良い場合もある。 ** UNCパスをカレントディレクトリにすることができるが、<code>Microsoft.PowerShell.Core\FileSystem::\\</code>とかいう謎のプレフィックスが付く。 ** 末尾が空白のフォルダについてはcdできず、Cygwinなどでそのようなフォルダをカレントディレクトリとして起動したとしても<code>C:\Windows\System32\WindowsPowerShell\v1.0</code>や<code>C:\Program Files\PowerShell\7</code>に移動してしまう。 ** 8.3形式のパスを強制的に長い名前に戻してcdしてしまう。その結果、長いフォルダをカレントディレクトリとして外部プログラムを実行できなくなる。 * PSModulePath環境変数の関係な気がするが、pwshの中でcmdを起動してその中でpowershellを起動したりするとPSReadLineモジュールが読み込めない(<code>PSReadline モジュールを読み込めません。コンソールは PSReadline なしで実行されています。</code>)とか言われたりする ==== 環境変数への置換 ==== cmdなどを使うと<code>%</code>で囲われた環境変数(<code>%PATH%</code>など)が置換される。<code>%</code>はファイル名にも普通に使える文字なので、注意が必要となる * 該当の名前の環境変数が定義されていたときのみ置換が行われ、定義されていなければそのままになる。 * <code>%</code><em><code>VAR</code></em><code>%</code>の置換は必ずしも<strong>cmdだけで行われるわけではない</strong>。「ファイル名を指定して実行」、VBSのWscript.Shellのrun、conhost、wtの引数などでも行われる。しかしCreateProcessやShellExecute(Ex)やレジストリ値では行われない(無理やりREG_EXPAND_SZにしたら展開されるが、後述の<code>%V</code>との適用順などは不明)。 * 一般には、環境変数には英数字とアンダースコアしか使えないとの説明もよくあるが、実際にはほとんどの記号が有効である。従って、<code>^%PATH^%</code>のようなキャレットによるエスケープはcmd向けにはある程度有効だが、<code>PATH^</code>という環境変数が定義されているとうまく動かない。 * (そういう変数が定義されていることは現実にほとんどないだろうし、それが保証できるなら以下の部分を読む必要はない) これを防ぐ方法はいくつかある。ちなみに、<code>%%</code>でのエスケープというのはバッチファイルの中だけの話で、今回の場面では以下より簡単なエスケープ方法は多分ないと思う。 <ol style="list-style-type: decimal;"> <li><p>既に定義されているかもしれない環境変数をバックアップし、一時的にその中身を<code>%</code>に変えて使用することでリテラル文字としての<code>%</code>を表現し、後で元に戻す。</p> <p>すなわち、<code>MY_PERCENT</code>のような変数の中身をバックアップし、<code>MY_PERCENT</code>の中身を<code>%</code>に変え、未知の文字列中の<code>%</code>をすべて<code>%MY_PERCENT%</code>にすることで<strong>この部分が置換によって</strong><strong><code>%</code></strong><strong>に変わるのを利用して</strong><code>%</code>をそのまま渡すということである(環境変数の置換は左から順に行われるため、たとえば<code>%MY_PERCENT%PATH%MY_PERCENT%</code>の<code>%PATH%</code>が置換されることはない)。受け渡しが終わったら<code>MY_PERCENT</code>の中身を復元する。</p> <p>これは、変数のバックアップとリストアを同じプロセス・スクリプト内で行える(<strong>呼び出した後に元に戻ってくる</strong>)場面で有用である。呼び出し先プロセスでリストアすることも不可能ではないと思うが、環境変数には任意の文字が入る可能性があることを考えるとエスケープが大変困難になることが予想される。</p></li> <li><p>VAR1, VAR2, VAR3, …のような無限個の変数を最初からチェックし、最初の未定義の変数を上記と同様に使用し、後で未設定にする。</p> <p>ほぼ上記と同じだが、内容のリストアでなく未設定にするだけなので、呼び出し先プロセスでも容易に行える。ただし実装が面倒である。</p></li> <li><p><code>%</code>を<code>==%</code>へと置換して、置換のプロセスを抜けた段階で元に戻す。</p> <ul> <li>これは、<strong>最初の文字以外に</strong><strong><code>=</code></strong><strong>が含まれる</strong>環境変数名は(実際に環境変数が設定されていたとしても絶対に)置換されないことがわかったためである。 <ul> <li><code>%=%</code>(あるいは、<code>%=VAR%</code>など)は少なくともcmdの対話シェル上だと置換されてしまうので<code>=%</code>へのエスケープだと不十分に見えるが、実際には<code>=</code>一つでも問題なく動く場合もあった。</li> <li>しかし、動かないときもあるので、多くの例では<code>==%</code>にエスケープしている。<code>=%</code>になっているものは、試した限りはそれでも問題なく動いているものである。</li> <li>[https://learn.microsoft.com/en-us/windows/win32/procthread/environment-variables Environment Variables - Win32 apps | Microsoft Learn]では「<code>=</code>は環境変数に使えない」と書かれていて、ダイアログからでも設定はできないが、レジストリだと無理やり設定できる。 <ul> <li>ユーザー環境変数に設定する分にはとりあえず問題なさそうだが、システム環境変数にレジストリから<code>=</code>(と、<code>=</code>を名前に含むいくつかの変数。どれが原因かは正確にはわからない)を無理やり追加したら<strong>0xc000021のブルースクリーンでWindowsが起動しなくなった</strong>(該当の変数を全て削除したら治った)のでこれはやってはいけない。</li></ul> </li> <li>ちなみに、<code>=</code>や<code>%</code>はUnix側では特殊文字ではないので、printfとの適用順はあまり気にする必要はない。</li></ul> </li> <li>この方法のメリットは、一度置換してしまえばその文字列を何度使いまわしても変化がないことが保証されることと、未定義の変数の探索や変数のリストアが必要ないことである。 <ul> <li>要は<code>%(=を含まない文字列)%</code>を含まない文字列だけを値にとる単射を構成すればいいだけなので、base64エンコーディングのようにしても構わない。</li></ul> </li> <li>ただ、<code>==%</code>を<code>%</code>に置換するという操作はPowerShellやshにとっては容易でもcmdにとっては不可能に近いので、最終的な呼び出し先がcmdであるときには採用しづらい。</li></ul> </li></ol> また、今回の記事ではcmdやvbsのRunを経由してコマンドを実行しているものが多くあるが、<strong>このスクリプトの内容自体もエスケープが必要</strong>である。つまり、例えば<code>cmd /c</code>の内側に<code>$mypath.Replace('%','%MY_PERCENT%')</code>というPowershellコードが含まれるなら、<code>','</code>や<code>MY_PERCENT</code>という環境変数が定義されていた時にその部分が置換されてしまう。これを回避するのはそこまで難しくなく、この例であれば<code>$mypath.Replace('%'.Trim('='),'%'.Trim('=')+'MY_PERCENT%')</code>のように<code>=</code>を含む無意味なコードを挿入すればよい。また、<code>%</code>をいったん<code>==%</code>にしている部分に関しては、<code>==%</code>ではなく<code>==%==</code>にすることで問題を避けられるだろう。無駄に複雑になるのでスクリプト例ではそのような措置はしていない。 ==== cmd ==== * UNCパス(<code>\\?\</code>だけでなく普通のUNCパスも含む)をカレントディレクトリにすることはできない(rdなど一部コマンドでの取り扱いは可能)。 * 末尾が空白のフォルダにcdすることはできないが、Cygwinなどでそのようなフォルダをカレントディレクトリとした状態でcmdを起動した場合は、そのフォルダをカレントディレクトリとして起動する(外部プログラムの起動はできないがcdなどの内部コマンドは有効で、一度でも出たら戻れない)。 * 各種のパース規則が本当に謎。特に引用符周りは地獄である。[https://thinca.hatenablog.com/entry/20100210/1265813598 cmd.exe のコマンドラインの仕様を解析してみた - 永遠に未完成]を読むとわかるが、例えば「ファイル名を指定して実行」で以下の挙動を確かめよう。 ** <code>cmd /k echo "a b"c d"</code>→<code>"a b"c d"</code>と出る ** <code>cmd /k echo "a & b"c d"</code>→<code>"a & b"c d"</code> ** <code>cmd /k echo "a b"c & d"</code>→<code>echo "a b"c</code>と<code>d"</code>がそれぞれ別々に実行される ** すなわち、<code>"</code>が一見特殊文字でないかのような振る舞いをする(そのまま出力される)割に、その前後で特殊文字の扱いが切り替わっている。 <span id="windows-terminal"></span> ==== Windows Terminal ==== * 以下、wtと略す(実行ファイル名がwt.exeなため)。パスが通っているものと仮定する。 * wtでは<code>;</code>が特殊文字になるようで、<code>\;</code>とエスケープが必要。[https://github.com/microsoft/terminal/issues/13264 https://github.com/microsoft/terminal/issues/13264] かな? ** <code>"</code>も<code>\"</code>にする必要がある。一方で、単一の<code>\</code>は(うしろに<code>;</code>や<code>"</code>などがなければ)そのまま<code>\</code>になるようである。大丈夫なのか…? * 起動時にカレントディレクトリがユーザーフォルダ(%USERPROFILE%)になる ** <code>-d</code>オプションで明示的に指定することができる ==== 連続空白 ==== 空白が連続するファイル名の対応は割と厄介である。Windows側では引数を<code>""</code>で囲うことで、またsh側では適宜<code>IFS=</code>と設定することで対処できる。 <span id="unicode対応"></span> ==== Unicode対応 ==== * UTF8を使おう * <code>chcp 65001</code>が一応使えるが、一旦シェル内部に入らないと使えないのでやや不便 ** 使わなくても大丈夫なときもある。あまりちゃんと理解できていないが、経験上、powershellで<code>$input</code>変数を用いて標準入力を受け取る際には<code>chcp 65001</code>が必要そう。 * /bin/printfやprintfの実行時には<code>LANG</code>を<code>en_US.UTF8</code>とかに設定する。 ** Cygwinなら不要? ==== デバッグ時 ==== * 管理者権限のものを試すときでもまず<code>-Verb Runas</code>を外して動作確認するとよい。 * 後述のwaitrunも役に立つだろう。 * 特殊文字に関しては、最初は空白のないフォルダ、次に空白のあるフォルダや連続空白のあるフォルダ、次にシングルクォートや<code>%PATH%</code>を含むフォルダなどとだんだん難しくしていくとよい。 * それとは別に、Unicode対応、<code>C:\</code>(ドライブ直下)対応、259文字のフォルダ対応(後述)などをチェックする * 難しい例は、例えば<code>z 𠮷𠮷%PATH% ' '' ` `` $PATH & && %%PATH%%[] ] [ ^ ^^ ' ; ;; 𩸽𩸽!PATH!#$%&'()=~{`+}_,.][;@^- - ‘ ’ ‚ ‛</code>のような感じ(適当)。こんなファイル名を現実に見ることはないが、エクスプローラー上で普通に入力できる内容である。 ** 「𠮷」はBMP外かつJIS外の漢字としては最も変換で出しやすいのでテストに重宝する。(𩸽はBMP外だがJIS外ではない(第4水準)) ==== その他 ==== * デフォルトシェルは、通常ユーザーはwtでも管理者の場合はconhostに設定されているっぽい? * <code>C:\</code>のようなドライブ直下のパスは末尾に<code>\</code>を付けなければいけないことが多い。たとえばwt -dの引数にする場合など。またcmdでcdコマンドや<code>CD</code>環境変数を見るときもドライブ直下のときだけは<code>\</code>が付いて返ってくる。 ** ただ、wt -d に<code>C:</code>が渡されるように見えるのになぜか動いているものもある。未調査。 * shやcmdなどの中に一旦入ってしまうと一部の環境変数などが書き換わってしまうかもしれないが、多少は許容することとする。 * コマンド例は網羅的でない可能性があり、色々な書き方を提示しようとあえて統一していないところもある。現実にはもっと簡単なやり方があるかもしれない。あくまで一例ということで。 * CygwinとGit Bashはほぼ同じなのでGit Bashを主に載せてCygwinは適宜差分のみ記述する。 * shは、特にベースのシェルとして使うにはエスケープの方式などでWindowsと相性が悪く使いづらい感じがする。逆にcmdは内部コマンドは地獄だがベースのシェルとしては意外と副作用が少なく、そう悪くはない。 * 以下の例ではCygwinやGit Bashの実行ファイルにはPATHを通していないことを想定している。<code>C:\cygwin64</code>あたりは適当に読み替えていただきたい。 * 2021年途中ごろまで、wtなどのストアアプリ系?の実行ファイル(Explorer上で0バイトになってるエイリアス)をGit Bashから実行できない(Permission deniedとなる)バグがあった。 [https://github.com/git-for-windows/git/issues/2675 https://github.com/git-for-windows/git/issues/2675] * ネットワークドライブは各ユーザー対象に割り当てられているせいなのか、<strong>管理者として実行すると利用できない</strong>らしい。割り当てられる前のネットワークパス自体は有効。 * wtを管理者権限で呼び出すときに(確率的に)ウインドウが最前面にならないことがある。chcpの有無で変わったりするなど詳細不明。conhostだとならない気がする。startrunなどを介して実行すると必ず最前面に出るようになるっぽい。 <span id="準備:-win-console-delegator"></span> == 準備: win-console-delegator == cmdやPowerShellやCygwinのbashなどのシェルを使えば、スクリプトを起動して複雑なコマンドを呼び出せたり、パイプ実行が可能になったりと色々便利だが、それだけのためには大仰すぎて、特殊文字の扱いなどがかえって仇になることもある。 そこで、シェル関連の操作に汎用的に使えるコマンドをいくつか作成して公開した。[https://github.com/ge9/win-console-tools https://github.com/ge9/win-console-tools] * 以前はC++で書いていた([https://github.com/ge9/win-console-delegator https://github.com/ge9/win-console-delegator])が、C#のほうが文字列の扱いなどが簡明。 ただし、これはあくまで筆者が独自に作ったものであり、Windows標準のもので何とかしようとするのが面白いところでもあるので、それほど積極的には使わない。今後出てくる例はすべて、これらを一切使わなくても(余計な環境変数が設定されてしまうかもしれないという点をのぞけば)同等の機能が実現できる。 <ul> <li><p>runother</p> <p>与えられた引数を特定の文字列とつなげて実行してくれるプログラム。この実行ファイル自体の名前を適宜変更して使用し、「特定の文字列」は別のファイルから読ませる。使用例は[[Windowsでのターミナル環境|Windowsでのターミナル環境]] を参照。</p></li> <li><p>runother-gui</p> <p>runotherとほぼ同じだが、コンソールアプリケーションではなくWindowsアプリケーション(黒いウインドウが出ない、cmd上で実行したときに終了待ちが行われないなど)。</p></li> <li><p>evalrun</p> <p>与えられたコマンドラインを実行し、その出力をそのままコマンドラインとして実行するコンソールアプリケーション。(テキストファイルからのコマンドラインの読み込みなどに使える)</p></li> <li><p>startrun</p> <p>与えられたコマンドラインをWindowsのデフォルトシェルで起動するだけのWindowsアプリケーション。</p></li> <li><p>waitrun</p> <p>与えられたコマンドラインを実行したあとキー入力を待ってから終了するコンソールアプリケーション。一瞬でウインドウが消えてしまうときのデバッグに使いやすい。</p></li> <li><p>hiderun</p> <p>コンソールウインドウ非表示の状態で与えられたコマンドを実行するWindowsアプリケーション。(GUIアプリケーションなどを与えた場合は非表示にならないかも)</p> <p>vbsのvbHideでも非表示にできるが、コンソールウインドウの表示位置をみると非表示のウインドウが一つ挟まっていることが分かるのに対して、こちらの場合はそうならない。</p></li> <li><p>piperun</p> <p>与えられたコマンドラインを最初の<code>|</code>とそれ以降の2つに区切ってそれぞれコマンドとしてパイプでつなげて実行するコンソールアプリケーション。ただし最初のコマンドのほうに<code>|</code>自体を入力したいときは<code>||</code>でエスケープする。また2つ目のコマンドの先頭の空白は除去される。</p></li> <li><p>piperunex</p> <p>piperunと似ているが、2つだけでなく3つ以上の任意個のパイプ実行を一度に行う。一見便利だが、コマンドの後ろ側に未知の文字列が渡される場合、<code>|</code>をエスケープする必要が生じる。</p> <p><code>piperun piperunex command1 || command2 || ... || commandN | unknown_commandline</code>のように外側を<code>piperun</code>で囲うと安全。</p></li> <li><p>adminrun</p> <p>与えられたコマンドラインを管理者として実行するWindowsアプリケーション。<code>ShellExecuteEx</code>では実行ファイルパスと引数は別々に指定しなければいけないので最低限の引数の解析を行っている。PowerShell経由で管理者権限で実行するときのような面倒なエスケープが全て不要になるため非常に使いやすいが、この記事ではこれに依存しないようにする。</p> <ul> <li>ちなみにこういうのもある。[https://github.com/mattn/sudo mattn/sudo: sudo for windows] これは、通常のUACのように別ウインドウで実行するのではなく、localhostのランダムなポートを使い、自分自身にポート番号を渡しつつ管理者権限で呼び出すことで標準入出力を転送し、元の端末でそれを読み書きできるという点で、linuxのsudoに近い。しかし標準入出力を介しているので、<code>cmd /c for /?</code>や<code>cmd /c pause</code>の挙動が変わってしまうという問題がある。また、呼び出し元でchcp 65001をしていると文字化けする。また、コマンドライン引数を一旦配列に分割してから組み直しているので情報が一部落ちている。あと、セキュリティ的にも懸念があるかもしれないが、これはそもそもLinuxのsudo自体がまずどうなの?という気持ちになった。(未解決)(参照: [https://twitter.com/e9g/status/1687385469921931264?s=20 https://twitter.com/e9g/status/1687385469921931264?s=20])</li></ul> </li> <li><p>uacrun</p> <p>startrunと同じだが、コンパイル時のマニフェスト設定でこのプログラム自体の実行に管理者権限を要求するようにしたので、adminrunと同じように使える(管理者権限で実行する対象が渡されたコマンドではなくuacrun自体になるという違いはある)</p></li> <li><p>pecho</p> <p>与えられたコマンドライン引数をそのままコンソールに出力する。→WindowsでUnicode文字(特にU+10000以上)を正しく表示(&ファイルに書き込み)するには、「出力先がコンソールならWriteConsoleW、そうでなければWriteFileを使用する」といった非常に面倒な実装が必要であり(参照: [https://twitter.com/mattn_jp/status/542581083242364928 https://twitter.com/mattn_jp/status/542581083242364928] など)、C++やC#では扱いづらい。そこでrustを用いて実装した。→[https://github.com/ge9/pure-echo-win pure-echo-win]</p></li> <li><p>printcd</p> <p>カレントディレクトリをコンソールに出力する。(ちなみにpwdの由来はprint working directoryなのでそれに倣った)</p></li></ul> <span id="注意:-当初は後述のnoworkingdirectoryを知らなかったので1の方法に頼っていたが実際には2の方法のほうが簡明なのでそちらを使うことを勧める1は残しておくが読む必要はあまりない"></span> == 注意: 当初は後述のNoWorkingDirectoryを知らなかったので1.の方法に頼っていたが、実際には2.の方法のほうが簡明なので、そちらを使うことを勧める。1.は残しておくが、読む必要はあまりない。 == <span id="1標準入力から受け取る場合"></span> == 1.標準入力から受け取る場合 == まず「標準入力からディレクトリのフルパスを受け取り、そのディレクトリで各種ターミナルを起動するコマンド」を紹介する。 そのようなコマンドを仮に<code>openterm</code>とすると、ExecuteCommand-Pipeを使用して、レジストリ(該当CLSIDのLocalServer32の既定の値)に以下のように書くことで、CLSIDが右クリックメニューのDelegateExecute値に使えるようになる。 <code>C:\path\to\ExecuteCommand4000.exe h openterm</code> <code>h</code>はコンソールウインドウを非表示にするExecuteCommand-Pipeのオプションである。 また、この<code>openterm</code>の部分は非常に長くなることがあり、その場合レジストリに書いて直接編集するのは手間がかかる(操作もしづらいし、更新を即座に反映させるために「<code>LocalServer32</code>」キーの名前などを変えて戻す必要もあって面倒)。そこで、runotherを使ってtxtに<code>openterm</code>の内容をそのまま書くことで、<code>openterm</code>の部分にそのexeの名前だけを書けばよくなる。 また、いくつかはCygwin/Git Bash向けに冒頭でcygpathによる変換を入れているが、これを取り除けば、Cygwin/Git Bash用のパスを受け取って動作するコマンドということになる。 では以下で、<code>openterm</code>部分についてそれぞれ紹介する。 * 直接コマンド記入でも<code>NoWorkingDirectory</code>を指定すれば<code>\\?\</code>に対応できるというのをこのセクションの大部分を書き終えてから知ったので、<strong>このセクションは内容の多さの割には実際の必要性はそこまで大きくないかもしれない</strong>。 <span id="git-bash"></span> === Git Bash === <span id="git-bashexeで開く"></span> ==== <code>git-bash.exe</code>で開く ==== この場合shの感覚で<code>git-bash.exe</code>に引数を渡せば勝手にminttyのウインドウで開いてくれるので最も楽である。 <code>cmd /c ""C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;/bin/env LANG=en_US.utf8 /bin/printf %q $(/bin/cat)" | "C:\Program Files\Git\usr\bin\xargs" -d '\n' -I {} "C:\Program Files\Git\git-bash.exe" -c "cd {};exec bash""</code> * Git BashのcygpathでGit Bash用のパスに変換 * printfでエスケープ、utf8を設定 * git-bash.exeに渡す <span id="デフォルトのターミナルで開く:-①startrunを使う"></span> ==== デフォルトのターミナルで開く: ①startrunを使う ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;/bin/env LANG=en_US.UTF8 /bin/printf %q $(/bin/cat)" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} startrun "C:\Program Files\Git\usr\bin\env.exe" MSYSTEM=MINGW64 "C:\Program Files\Git\usr\bin\bash.exe" --login -i -c 'cd {};exec bash'"</code> * <code>git-bash.exe</code>や<code>C:\Program Files\Git\bin</code>のbashではなく/usr/binのbash.exeを使う。以後同じ。 <span id="デフォルトのターミナルで開く:-②cmdのstartを使う"></span> ==== デフォルトのターミナルで開く: ②cmdのstartを使う ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;/bin/env LANG=en_US.UTF8 /bin/printf %q $(/bin/sed 's/\\^/^^/g;s/%/=%/g;s/&/^&/g')" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} cmd //c start "" "C:\Program Files\Git\usr\bin\env.exe" MSYSTEM=MINGW64 "C:\Program Files\Git\usr\bin\bash.exe" --login -i -c 'cd "$(echo {} ^| sed s/=%/%/g)";exec bash'"</code> * この場合、cmdによる環境変数への置換を避ける必要があるほか、キャレットによるエスケープにも対応する必要がある。 * Git Bashからcmdを実行しているので<code>//c</code>のところのスラッシュは2つ。 ==== conhostで開く ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;/bin/env LANG=en_US.UTF8 /bin/printf %q $(/bin/sed 's/%/=%/g')" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} conhost "C:\Program Files\Git\usr\bin\env.exe" MSYSTEM=MINGW64 "C:\Program Files\Git\usr\bin\bash.exe" --login -i -c 'cd "$(echo {}|sed s/=%/%/g)";exec bash'"</code> conhostによる環境変数の置換を抑制。 <span id="windows-terminalで開く"></span> ==== Windows Terminalで開く ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;LANG=en_US.UTF8 printf %q $(/bin/sed 's/%/=%/g')" | "C:\Program Files\Git\usr\bin\sed.exe" "s/;/\\\\;/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} wt "C:\Program Files\Git\usr\bin\env.exe" MSYSTEM=MINGW64 "C:\Program Files\Git\usr\bin\bash.exe" --login -i -c "IFS=\\;cd $(echo {}|sed s/=%/%/g) \\;exec bash""</code> 上記に加えてセミコロンへの対応が必要なのと、こっちは/bin/printfじゃなくて組み込みのprintfでこうしないとだめだった。 <span id="cygwin"></span> ==== Cygwin ==== 付属のminttyを使うものだけはGit Bashと割と差があるので載せる。 <code>cmd /c ""C:\cygwin64\bin\cygpath" -f - | "C:\cygwin64\bin\sh.exe" -c "IFS=;/bin/printf %q $(/bin/cat)" | "C:\Program Files\Git\usr\bin\xargs" -d '\n' -I {} "C:\cygwin64\bin\mintty.exe" -e "C:\cygwin64\bin\bash.exe" --login -i -c "cd {};exec bash""</code> <span id="powershell"></span> === PowerShell === <span id="デフォルトシェル-startrun"></span> ==== デフォルトシェル, startrun ==== * パスのエスケープがおかしい気がするが、なぜか動いている <code>cmd /c ""C:\Program Files\Git\usr\bin\sed.exe" "s/'/''/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} startrun powershell -noexit -command Set-Location -literalPath "'{}'""</code> <span id="デフォルトシェル-cmdのstart"></span> ==== デフォルトシェル, cmdのstart ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\sed.exe" "s/'/''/g;s/%/=%/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} cmd //c start "" powershell -noexit -command Set-Location -literalPath '\'{}\'.Replace(\'=^%\',\'%\')'"</code> <span id="cmd-1"></span> === cmd === <span id="windows-terminal-1"></span> ==== Windows Terminal ==== <code>cmd /c chcp 65001 & powershell -Command "$mypath=($input | ForEach-Object { return $_ });$bak=$env:MY_PERCENT; $env:MY_PERCENT = '%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %A in (`\"$($mypath.Replace('%', '%MY_PERCENT%'))`\") do @echo %~sA\" };$env:MY_PERCENT = $bak;wt -d ($mypath -replace ';', '\;') cmd"</code> <ul> <li><p>[[Windowsのパス長さ制限に関して|Windowsのパス長さ制限に関して]] でも書いた通り、Windows側は<strong>ちょうど259文字</strong>の長さのフォルダだけは(その必要があるにもかかわらず)8.3形式を使った短いパスにして渡してくれない。そこで、パス長さが258を超えていればcmdに渡して8.3形式の名前に変換する処理をPowerShell側で行う。この際はエスケープされていない正確なパス名をcmdに渡さなければならないので<code>MY_PERCENT</code>の値を一時的に<code>%</code>に設定する方法をとっている。</p> <ul> <li>念のため再確認しておくが、この<code>'%MY_PERCENT%'</code>の部分は<code>'%'.Trim('=')+'MY_PERCENT%')</code>などと変えておかないと、<code>MY_PERCENT</code>が定義されていた場合に正しく動作しない。</li> <li>cmdでは<strong>ちょうど258文字</strong>のフォルダでもdirが失敗するなど挙動が不自然であるため、もう少し保守的に<strong>257</strong>を超えていれば短くするという仕様でもいいかもしれない。</li></ul> </li> <li><p>(追記)8.3形式のパスを取得するのは普通にPowerShell内でもできるらしい…。以下のようになる。</p> <p><code>if ($mypath.Length -gt 258) { $mypath = (New-Object -ComObject Scripting.FileSystemObject).GetFolder($mypath).ShortPath}</code></p></li></ul> 259文字のフォルダを無視するなら以下のように簡潔に済む。 <code>cmd /c chcp 65001 & powershell -Command wt '-d' $input.Replace(';','\;') cmd</code> ==== デフォルトシェル ==== これは案外難しい。なぜなら、cmd内でcdさせるのは%のエスケープの関係で難しく(cmd内で=を含む文字列を置換することがどうやってもできなさそう(外部プログラムを呼び出すのも難しそうだった)なので、未定義の変数を探索するやり方しかない)、Start-Processの-WorkingDirectoryやCygwinのshは8.3形式に対応していないからである。要は「与えられたディレクトリ(8.3形式のパスが含まれるかもしれない)をカレントディレクトリとして与えられたコマンドを実行する」だけやってくれるプログラムがあればよく、これ自体はそう難しくないことのはずだが、現状、Windows標準環境でこれができるのは自前でプログラムをコンパイルする以外だと(pwshを入れていいならpwshと)<strong>vbsしかない</strong>ようである。結局、この部分をついでにやってくれるWindows Terminalのほうが楽ということになる。 たとえば以下のようなvbsを用意する。 <syntaxhighlight style="margin-bottom:0.2em;" lang="vb.net">Dim objShell Set objShell = CreateObject("WScript.Shell") objShell.CurrentDirectory = WScript.Arguments(1) objShell.Run(WScript.Arguments(0)),,False</syntaxhighlight> <div style='text-align: center;'>startatdir.vbs</div> すると以下のように書ける。 <code>cmd /c chcp 65001 & powershell -Command "$mypath=($input | ForEach-Object { return $_ });$bak=$env:MY_PERCENT;$env:MY_PERCENT = '%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %A in (`\"$($mypath.Replace('%', '%MY_PERCENT%'))`\") do @echo %~sA\" };$env:MY_PERCENT = $bak; wscript 'C:\path\to\startatdir.vbs' cmd ('\"'+$mypath+'\"')"</code> なお<code>startatdir.vbs</code>のパス指定はフルパスが必要そう(ただしここはcmd /cの中なので<code>%USERPROFILE%</code>とかを使って書いてもよい) <span id="11標準入力から受け取る場合---管理者権限あり"></span> == 1.1.標準入力から受け取る場合 - 管理者権限あり == 上記の続きで、こちらは管理者権限ありのもの。 <span id="git-bash-1"></span> === Git Bash === <span id="デフォルトシェル-1"></span> ==== デフォルトシェル ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;/bin/env LANG=en_US.UTF8 /bin/printf %q $(/bin/cat)" | "C:\Program Files\Git\usr\bin\sed.exe" "s/'/''/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} powershell -Command Start-Process -Verb Runas -Filepath '"C:\Program Files\Git\usr\bin\env.exe"' -ArgumentList '"MSYSTEM=MINGW64"', '"`"C:\Program Files\Git\usr\bin\bash.exe`""' , '--login', '-i' , '-c', "'\"cd {}; exec bash\"'" "</code> * Start-Process自体がstartみたいなものなので、引数にコンソールアプリケーションを指定したらデフォルトシェルで開かれる。 * 最後のところ、「<code>"'\"cd {}; exec bash\"'"</code>」のかわりに「<code>'\'"cd {};exec bash"\''</code>」 とか「<code>'\'\"cd {};exec bash"\''</code>」 でも動くのは謎。シングルクォーテーションのエスケープ規則がわからない。参考→[https://twitter.com/e9g/status/1678283164689760256 https://twitter.com/e9g/status/1678283164689760256] <span id="windows-terminal-2"></span> ==== Windows Terminal ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;LANG=en_US.UTF-8 printf %q $(/bin/cat)" | "C:\Program Files\Git\usr\bin\sed.exe" "s/'/''/g;s/%/==%/g;s/;/\\\\;/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList "'"'"C:\Program Files\Git\usr\bin\env.exe"'"'" , '"MSYSTEM=MINGW64"' , "'"'"C:\Program Files\Git\usr\bin\sh.exe"'"'" , "'--login'", "'-c'", "'"'"IFS=\\;cd $(echo {}| /bin/sed s/==%/%/g) \\; exec bash"'"' " "</code> ==== adminrunを使う ==== <code>piperunex "C:\Program Files\Git\usr\bin\cygpath" -f - | "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=; /bin/env LANG=en_US.UTF8 /bin/printf %q $(/bin/cat)" | "C:\Program Files\Git\usr\bin\sed.exe" "s/%/==%/g;s/;/\\\\;/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} adminrun wt "C:\Program Files\Git\usr\bin\env.exe" "MSYSTEM=MINGW64" "C:\Program Files\Git\usr\bin\sh.exe" --login -c "IFS=\\;cd $(echo {}|| /bin/sed s/==%/%/g) \\; exec bash"</code> <span id="powershell-1"></span> === PowerShell === <span id="windows-terminal-3"></span> ==== Windows Terminal ==== <code>cmd /c ""C:\Program Files\Git\usr\bin\sed.exe" "s/'/''''/g;s/%/==%/g;s/;/\\\\;/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList 'powershell', '-noexit', '-command', 'Set-Location', '-LiteralPath', "'''\"{}\"''.Replace(''==%'',''%'')'" "</code> * Cygwin系なしで <code>cmd /c "chcp 65001 & powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList 'powershell', '-noexit', '-command', 'set-location', '-literalpath', ('\"'''+($input -replace '''', '''''' -replace '%', '==%' -replace ';', '\;' )+'''.Replace(''==%'',''%'')\"') "</code> <span id="cmd-2"></span> === cmd === <span id="デフォルトシェル-2"></span> ==== デフォルトシェル ==== 再び、startatdir.vbsを使う。 <code>cmd /c chcp 65001 > nul & powershell -Command "$mypath=($input | ForEach-Object { return $_ });$bak=$env:MY_PERCENT; $env:MY_PERCENT = '%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %A in (`\"$($mypath.Replace('%', '%MY_PERCENT%'))`\") do @echo %~sA\" };$env:MY_PERCENT = $bak; Start-Process -Verb Runas -Filepath wscript -Argumentlist '//Nologo', 'C:\path\to\startatdir.vbs', 'cmd', ('\"'+$mypath+'\"')"</code> <span id="windows-terminal-4"></span> ==== Windows Terminal ==== <code>cmd /c chcp 65001 & powershell -Command "$mypath=($input | ForEach-Object { return $_ }); $bak=$env:MY_PERCENT; $env:MY_PERCENT = '%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %A in (`\"$($mypath.Replace('%', '%MY_PERCENT%'))`\") do @echo %~sA\" };$env:MY_PERCENT = $bak;Start-Process -Verb Runas -Filepath wt -Argumentlist '-d', ('\"'+($mypath -replace '\\$','\\\\' -replace ';', '\;')+'\"'), cmd"</code> この場合は<code>C:\</code>のための対処(<code>-replace '\\$','\\\\'</code>)が必要である。259文字のフォルダを無視するなら以下の通り。 <code>cmd /c "chcp 65001 & powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList '-d', ('\"'+($input -replace '\\$','\\\\').Replace(';','\;')+'\"'), cmd"</code> <span id="2直接コマンド記入の右クリックメニュー"></span> == 2.直接コマンド記入の右クリックメニュー == レジストリの<code>command</code>キーの既定の値に直接コマンドを記入する方式である。 デフォルトでは、右クリックメニューを<strong>押した時点</strong>でのフォルダ(フォルダを右クリックするメニューからだと<strong>対象フォルダの親</strong>、フォルダ背景を右クリックするメニューなら対象フォルダ)をカレントディレクトリとしてコマンドが起動される。そのため<code>\\?\</code>を付けなければならないような長大なパスのフォルダでは動作しない。しかし、<code>shell\xxxx</code>キーに<strong><code>NoWorkingDirectory</code></strong><strong>値</strong>を設定することで、カレントディレクトリが必ず<code>C:\Windows\System32</code>に設定されるようになるので動くようになる。 対象のディレクトリはどう取得するかというと、レジストリに書き込んだ値のうち<strong><code>%V</code></strong><strong>という部分が対象ディレクトリに書き換えられて</strong>コマンドが実行される。この<code>%V</code>もC:などの場合は<code>C:\</code>になる。コマンドの中で<strong>そのまま%という文字を使いたい場合はバッチファイルと同様に%%とエスケープする必要がある</strong>ようである(これはどこにも書いてない?)。 しかし、こうして渡される(特殊文字を含むかもしれない)ディレクトリ名を完璧に取得するのは意外と難しい。cmdに解釈させると<code>%</code><em><code>VAR</code></em><code>%</code>が置換されてしまうし、シングルクォートとバッククォートが両方含まれているのでpowershellやshに渡すのも簡単ではない。 コツは、powershell -commandやsh -cの中に直接書くのではなく、<strong>スクリプトへのコマンドライン引数として渡した上で、スクリプト内で引数として取得する</strong>ことである。それぞれ、基本形(そのまま出力するだけ)は以下のようになる。 <ul> <li><p>powershell</p> <p><code>powershell -Command "& {echo $args[1].Trim('\"')}" --%% "\"%V\""</code></p> <ul> <li><code>--%</code>というトークンを入れることでその後のパラメータの解析を行わないようにできる。args[0]は<code>--%</code>になるのでargs[1]を使う。レジストリに書く際は<code>%</code>をエスケープして<code>--%%</code>。</li> <li><code>--</code>でも同様のことができる?<code>powershell -Command "& {echo $args[0].Trim('\"')}" -- "\"%V\""</code>(args[<strong>0</strong>]になっていることに注意)</li> <li>というか、<code>--%</code>で書き進めてしまったが、本当は<code>--</code>のほうが安定するかもしれない。</li></ul> <p>あるいは<code>$MyInvocation.Line</code>を使う方法もある。この場合、powershellへの引数が全て(この例なら<code>-noexit -Command "& {$spl = ...</code>のところから)取得されるので、目当ての部分を取り出すためにここでは最後から2番目の<code>"</code>と最後の<code>"</code>の間を取得するという風にしている。</p> <p><code>powershell -noexit -Command "& {$spl = $MyInvocation.Line -split'\"' ; Set-Location -LiteralPath $spl[$spl.Length-2]}" --%% "\"%V\""</code></p> <p>昔のPowerShell(2くらい)で試してみたらこちらでないと<code>`</code>や<code>&</code>あたりの処理がうまくいかない場合があった。argsを使うものからの書き換えは容易である。</p></li> <li><p>sh</p> <p><code>sh -c "IFS=;echo $*" -- "%V</code></p> <ul> <li>最後が<code>"%V</code>と閉じられていないのはミスではなく、<code>"%V"</code>としてしまうと<code>C:\</code>のときに<code>C:"</code>が渡されてしまうのでそれを避けるためである(これはかなりトリッキーなのでもう少し真面目にやってもいいとは思う)。</li> <li>UNCパスの最初の<code>\\</code>が<code>\</code>に変わってしまう(手元では、Cygwinの場合は<code>\\?\</code>のみで発生している?)という問題があり、適宜置換する必要がある。</li></ul> </li></ul> これらの書き方はいわゆる「ワンライナー」的なものであるが、もちろん外部にスクリプトファイルを用意してもよい。 また、外部にスクリプトを用意してよいという条件であれば、<strong>vbsも使用可能</strong>である。 <ul> <li><p>この場合、まずレジストリには</p> <p><code>wscript "C:\path\to\script.vbs" "%V"</code></p> <p>などと書く。スクリプトの内容は上記のレジストリの内容をほぼそのままWscript.ShellでRunすればよいが、<code>%V</code>のかわりにWscript.Arguments(0)を挿入するのと、<code>%</code>を<code>%%</code>にエスケープしなくてよいのと、VBSなので文字列内の<code>"</code>は<code>""</code>に変えなければならない。これに加え、Runの引数に含まれる<code>%</code><em><code>VAR</code></em><code>%</code>が置換の対象となるため、対象フォルダの文字列については<code>==%</code>への置換でエスケープして、呼び出し先のpowershellなどで元に戻す。</p></li> <li><p>必要に応じてRunのオプションでvbHideを指定する。</p></li> <li><p>というか別に258文字を超えていたら云々みたいな処理も全部vbs側でやってしまってもよい(面倒なのでそういう例は載せていない)</p></li></ul> バッチファイルに渡すのは、バッチファイルをcmdが呼び出す時点で<code>%PATH%</code>のようなファイル名の環境変数は既に展開されてしまうので、どうやっても不可能。 他には、(hiderunと)piperunとpechoを併用するという方法もあり、それらの実行ファイルを用意する必要があるという以外は綺麗にできる方法である。 いずれにしろ、一旦取得できてしまえばあとは前述の<code>openterm</code>に流し込むだけでよい。ただ、実際にはわざわざ<code>openterm</code>に流し込まなくても(パイプを使わなくても)もっと簡単に起動できることもあるのでそちらを中心に紹介する。 * commandの最初で指定する(メインの)実行ファイル(先ほどなら<code>powershell</code>や<code>sh</code>)をファイル名単体で書く場合は、それが(メニューをHKCUで設定するならHKCUの(最近のWindowsならHKLMのでも可?)、HKLMで設定するならHKLMの)<strong>App Pathsキー</strong>に登録されていなければならない(<strong>PATHは通っていなくてもいい</strong>)。登録されていない場合はフルパスで指定する必要がある。 === 設定例 === ==== 管理者権限なし ==== <ul> <li><p>PowerShell</p> <p>デフォルトターミナルで</p> <p><code>powershell -noexit -Command "& {Set-Location -LiteralPath $args[1].Trim('\"')}" --%% "\"%V\""</code></p> <ul> <li><p>参考までに、Windowsにもともと入っているメニューでは以下のようになっている。</p> <p><code>powershell.exe -noexit -command Set-Location -literalPath '%V'</code></p> <p>これだとシングルクォートと連続スペースに対応できない。連続スペースだけなら<code>'%V'</code>を<code>"'%V'"</code>に変えると解決できる。<code>%$[]`</code>あたりは見た感じ問題なし。</p> <ul> <li>以下の解決策でもまだ不完全 [https://superuser.com/questions/1310258/open-powershell-window-here-file-explorer-context-menu-command-breaks-with-fol "Open PowerShell window here" File Explorer context-menu command breaks with folder names with apostrophes (single quotes) - Super User]</li></ul> </li> <li><p>この問題は[https://github.com/PowerShell/PowerShell/issues/6598 https://github.com/PowerShell/PowerShell/issues/6598]や[https://github.com/PowerShell/PowerShell/pull/6660 https://github.com/PowerShell/PowerShell/pull/6660] で扱われており、結局pwshでは-WorkingDirectoryというパラメータが追加された。[https://github.com/PowerShell/PowerShell/issues/14091 https://github.com/PowerShell/PowerShell/issues/14091]にある以下の文字列をレジストリに書いたところ、正しく動作したので、pwshではこれでよい。</p> <p><code>pwsh.exe -NoExit -RemoveWorkingDirectoryTrailingCharacter -WorkingDirectory "%V!" -Command "$host.UI.RawUI.WindowTitle = 'PowerShell 7 (x64)'"</code></p></li></ul> <p>wtで</p> <p><code>hiderun powershell -Command "& {wt powershell -noexit -Command Set-Location -LiteralPath ('\"'''+($args[1] -replace '''', '''''' -replace '%%', '==%%' -replace ';', '\;' )+'''.Replace(''==%%'',''%%'')\"')}" --%% "\"%V\""</code></p></li> <li><p>cmd</p> <p>wtで。標準入力のときとほぼ同じ。</p> <p><code>powershell -Command "& {$bak=$env:MY_PERCENT;$mypath=$args[1].Trim('\"'); $env:MY_PERCENT = '%%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %%A in (`\"$($mypath.Replace('%%', '%%MY_PERCENT%%'))`\") do @echo %%~sA\" };$env:MY_PERCENT = $bak;wt -d ($mypath -replace ';', '\;') cmd}" --%% "\"%V\""</code></p> <ul> <li><p>デフォルトターミナルで</p> <p>startatdir.vbsを使う。</p> <p><code>powershell -Command "& {$bak=$env:MY_PERCENT;$mypath=$args[1].Trim('\"'); $env:MY_PERCENT = '%%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %%A in (`\"$($mypath.Replace('%%', '%%MY_PERCENT%%'))`\") do @echo %%~sA\" };$env:MY_PERCENT = $bak; wscript 'C:\path\to\startatdir.vbs' cmd ('\"'+$mypath+'\"')}" --%% "\"%V\""</code></p></li> <li><p>Windowsにもともと入っているメニューでは以下。</p> <p><code>cmd.exe /s /k pushd "%V"</code></p> <p>これは(<code>\\?\</code>が付くパスを除いて)ほとんどの場合に正しく機能するが、<code>%PATH%</code>のような文字列が含まれているフォルダではうまくいかない。ちなみにpushdによりUNCパスには自動的にネットワークドライブが割り当てられる。</p></li></ul> </li> <li><p>Git Bash</p> <ul> <li><p>mintty(git-bash.exe使用)</p> <p><code>"C:\Program Files\Git\git-bash.exe" -c "IFS=;cd $(echo $*|/bin/sed 's/\\\\/\\\\\\\\/'|/bin/cygpath -f -);exec bash" -- "%V</code></p> <ul> <li><p>Git Bashのインストーラにより設定される「Git Bash Here」メニューでは以下のようになっている。</p> <p><code>"C:\Program Files\Git\git-bash.exe" "--cd=%v."</code></p> <p>これは、特殊文字にはすべて対応しているが、(NoWorkingDirectoryをつけたとしても)<code>\\?\</code>で始まるパスではうまくいかない。最後の「.」はよくわからないがこれを付けておくと<code>--cd</code>がうまくやってくれるっぽい。</p></li></ul> </li> <li><p>デフォルトシェルで</p> <p><code>"C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;cd $(echo $*|/bin/sed 's/\\\\/\\\\\\\\/'|/bin/cygpath -f -);export MSYSTEM=MINGW64;exec /bin/bash --login" -- "%V</code></p></li> <li><p>wtで</p> <p><code>hiderun "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=;LANG=en_US.UTF8; echo $*|/bin/sed 's/\\\\/\\\\\\\\/'|/bin/cygpath -f -|printf %%q $(/bin/sed 's/%%/=%%/g')|/bin/sed 's/;/\\\\;/g'|/bin/xargs -d '\n' -I {} -- wt \"C:\Program Files\Git\usr\bin\env.exe\" MSYSTEM=MINGW64 \"C:\Program Files\Git\usr\bin\bash.exe\" --login -i -c 'IFS=\\;cd $(echo {}|sed s/=%%/%%/g)\\;exec bash'" -- "%V</code></p></li></ul> </li> <li><p>Cygwin</p> <ul> <li><p>デフォルトシェルで</p> <p><code>"C:\cygwin64\bin\sh.exe" --login -c "IFS=;cd $(echo $*|/bin/sed 's/\\\\?/\\\\\\\\?/'|/bin/cygpath -f -);exec bash" -- "%V</code></p> <ul> <li>置換の対象を<code>\\?\</code>のみとするためsedの引数に<code>?</code>が増えている</li></ul> </li> <li><p>wtで</p> <p><code>hiderun "C:\cygwin64\bin\sh.exe" -c "IFS=;echo $*|/bin/sed 's/\\\\?/\\\\\\\\?/'|/bin/cygpath -f -|printf %%q $(/bin/sed 's/%%/=%%/g')|/bin/sed 's/;/\\\\;/g'|/bin/xargs -d '\n' -I {} -- wt \"C:\cygwin64\bin\bash.exe\" --login -i -c 'IFS=\\;cd $(echo {}|sed s/=%%/%%/g)\\;exec bash'" -- "%V</code></p></li></ul> </li></ul> ==== 管理者権限 ==== <ul> <li><p>PowerShell</p> <p><code>powershell -Command "& {Start-Process -Verb Runas -Filepath wt -Argumentlist powershell, -noexit, -command, Set-Location, -LiteralPath, ('\"'''+($args[1].Trim('\"') -replace '''', '''''' -replace ';', '\;' -replace '%%', '==%%')+'''.Replace(''==%%'',''%%'')\"')}" --%% "\"%V\""</code></p> <p>環境変数の置換を抑止する必要がある。デフォルトシェルで起動するならこれは不要(で、wtでないので<code>;</code>のエスケープも不要)で、以下のようになる。</p> <p><code>powershell -Command "& {Start-Process -Verb Runas -Filepath powershell -Argumentlist '-noexit', '-command', Set-Location, '-LiteralPath', ('\"'''+($args[1].Trim('\"') -replace '''', '''''' )+'''\"')}" --%% "\"%V\""</code></p> <ul> <li><p>MyInvocationを使う例</p> <p><code>powershell -Command "& {$spl = $MyInvocation.Line -split'\"'; Start-Process -Verb Runas -Filepath powershell -Argumentlist '-noexit', '-command', Set-Location, '-LiteralPath', ('\"'''+($spl[$spl.Length-2] -replace '''', '''''' )+'''\"')}" --%% "\"%V\""</code></p></li> <li><p>vbsの例</p> <p>次の節で解説する「(自分が起動された)カレントディレクトリでの起動」にも対応している(引数がない場合)。</p> <syntaxhighlight lang="vb.net">Dim objShell Dim curDir Set ws = CreateObject("Wscript.Shell") Dim dir If WScript.Arguments.Count = 0 Then dir = ws.CurrentDirectory Else dir = Wscript.Arguments(0) End If dir = Replace(dir,"%","==%") ws.run "powershell -Command ""& {Start-Process -Verb Runas -Filepath wt -Argumentlist powershell, -noexit, -command, Set-Location, -LiteralPath, ('\""'''+($args[1].Trim('\""') -replace '''', '''''' -replace ';', '\;' )+'''.Replace(''==%'',''%'')\""')}"" --% ""\"""&dir&"\""""", vbHide Set objShell = Nothing</syntaxhighlight></li></ul> </li> <li><p>cmd</p> <p><code>powershell -Command "& {$bak=$env:MY_PERCENT;$mypath=$args[1].Trim('\"'); $env:MY_PERCENT = '%%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %%A in (`\"$($mypath.Replace('%%', '%%MY_PERCENT%%'))`\") do @echo %%~sA\" };$env:MY_PERCENT = $bak;Start-Process -Verb Runas -Filepath wt -Argumentlist '-d', ('\"'+($mypath -replace '\\$','\\\\' -replace ';', '\;')+'\"'), cmd}" --%% "\"%V\""</code></p> <p>この場合も<code>C:\</code>のための対処(<code>-replace '\\$','\\\\'</code>)が必要である。デフォルトシェルなら、startatdir.vbsを使う。</p> <p><code>powershell -Command "& {$bak=$env:MY_PERCENT;$mypath=$args[1].Trim('\"'); $env:MY_PERCENT = '%%';if ($mypath.Length -gt 258) { $mypath = cmd /c \"for %%A in (`\"$($mypath.Replace('%%', '%%MY_PERCENT%%'))`\") do @echo %%~sA\" };$env:MY_PERCENT = $bak;Start-Process -Verb Runas -Filepath wscript -Argumentlist '//Nologo', 'C:\path\to\startatdir.vbs', 'cmd', ('\"'+$mypath+'\"')}" --%% "\"%V\""</code></p></li> <li><p>Git Bash</p> <p>理論上はワンライナーでも書けるが、エスケープが面倒すぎるのでファイルに書いたほうがよいだろう。まずレジストリの中身は以下のようにする。</p> <p><code>"C:\Program Files\Git\usr\bin\sh.exe" "C:\path\to\gb-wt-admin.sh" "%V"</code></p> <p>で、<code>gb-wt-admin.sh</code>の中身は以下。</p> <syntaxhighlight lang="bash">#!/bin/sh IFS= LANG=en_US.UTF-8 echo $*|/bin/sed 's/\\/\\\\/'|/bin/cygpath -f -|printf %q $(/bin/cat) |/bin/sed "s/'/''/g;s/%/==%/g;s/;/\\\\;/g"|/bin/xargs -d '\n' -I {} powershell -Command Start-Process -Filepath wt -ArgumentList "'"'"C:\Program Files\Git\usr\bin\env.exe"'"'" , '"MSYSTEM=MINGW64"' , "'"'"C:\Program Files\Git\usr\bin\sh.exe"'"'" , "'--login'", "'-c'", "'"'"IFS=\;cd $(echo {}| /bin/sed s/==%/%/g) \; exec bash"'"'"</syntaxhighlight> <p>Cygwinも同様である。</p></li></ul> <span id="vs-codeのメニューおまけ"></span> ==== VS Codeのメニュー(おまけ) ==== 「Code で開く」メニューのcommand値は以下のように設定されている。 <code>"C:\Program Files\Microsoft VS Code\Code.exe" "%V"</code> これは特殊文字などに関しても問題なく動作するうえ、実はそのまま<code>\\?\</code>が付いたパスにも適用可能である。従って、<code>VSCode</code>キーに<code>NoWorkingDirectory</code>値を設定する(設定の書き換えまたはHKCUによるオーバーライド)だけで、長大フォルダでもVS Codeが使えるようになる。ターミナルの起動には失敗してしまうがファイル・フォルダの作成・編集などの基本操作がGUIでできるので意外と便利(もちろん、他のBetter Explorer的なツールを使う手もあるだろう)。 <span id="3-エクスプローラーのアドレスバーから"></span> == 3. エクスプローラーのアドレスバーから == このときは、末尾が空白のパスや<code>\\?\</code>が必要な長大パスやちょうど259文字のフォルダはそもそも対応していないので考えなくてよい。しかし通常のネットワークファイル向けのUNCパス(<code>\\192.168.1.1\disk</code>など)に対応する必要はあるので、cmdをベースにすることはできない(cmdの中に入った時点でカレントディレクトリが変わってしまう)。また<code>``</code>が含まれるパスをカレントディレクトリにできないことから、powershell.exeもベースとしては使えない。 なので、パイプを使って先ほどの<code>openterm</code>にむけて流し込もうと思ったら選択肢はpwsh.exeかpiperunかCygwin系だけである。それぞれの書き方は以下のようになる。 <ul> <li><p>pwsh</p> <p><code>pwsh -Command $pwd.Path.Replace('Microsoft.PowerShell.Core\FileSystem::\\', '\\') | openterm</code></p></li> <li><p>piperun</p> <p><code>piperun pwsh -Command $pwd.Path.Replace('Microsoft.PowerShell.Core\FileSystem::\\', '\\') | openterm</code></p> <p>上とほぼ変わっていないが、pwshはディレクトリの取得だけで使っているので<code>openterm</code>のところで環境変数が汚染されるのを防げる。</p></li> <li><p>Cygwin/Git Bash</p> <p><code>"C:\Program Files\Git\usr\bin\sh.exe" -c "cmd //c \"chcp 65001 > nul\"| pwd | /bin/cygpath -w -f - | openterm"</code></p> <ul> <li>一つ問題があり、hiderunを使う(コンソールを非表示にする)とchcpが効かないようで、<code>openterm</code>でPowerShellの<code>$input</code>を使っているとUnicode文字が化けてしまう。</li> <li>先ほどの<code>openterm</code>部分で先頭にcygpathが入っているものを使うときは、そちらのcygpathとこちらでのcygpath -wは打ち消し合って無駄なので消してよい。</li></ul> </li></ul> このほかにvbs(wscript)もカレントディレクトリの取得のところは問題なくやってくれるので、それをさっきのWscript.Arguments(0)のかわりに使えばよい。 最初に表示されるコンソールを非表示にしたければ、<code>hiderun</code>を付けてrunother経由で実行するのが手軽だが、前述の通りvbsも拡張子無しで呼び出せるように設定できるので、そうした上でvbHideで隠してもよい。 <span id="管理者権限なし-1"></span> === 管理者権限なし === <span id="cmd-3"></span> ==== cmd ==== これは普通にcmdと打てばよい。 (デフォルトシェルがconhostだとして)wtで開きたいときは、;をエスケープして-dで渡す。以下の通りbatファイルを作ってpathを通す。 <code>chcp 65001 & cd | powershell -Command wt -d $input.Replace(';','\;') cmd</code> wtではなくconhostの場合はconhost cmdだけでよい。batにするなら<code>start "" conhost cmd</code>でよい。 コンソール非表示ならcmd /cをつけてhiderunする。runotherで、txtの中身は以下の通り。 <code>hiderun cmd /c chcp 65001 & cd | powershell -Command wt -d $input.Replace(';','\;') cmd</code> <span id="powershell-pwsh"></span> ==== powershell, pwsh ==== pwshはそのままpwshと打てばよい。powershell及びwtやconhostを明示的に指定する場合について以下で述べる。 たとえばwtでpwshなら、runotherを使って、txtの内容は以下。 <code>hiderun pwsh -Command wt -d $pwd.Path.Replace('Microsoft.PowerShell.Core\FileSystem::\\', '\\').Replace(';', '\;') pwsh</code> powershellの場合は<code>``</code>に対応するため起動後に移動する。runotherでpiperunを使って、txtは以下。最終的に起動する方のpowershellで<code>==%</code>を<code>%</code>に戻している。 <code>hiderun piperunex cmd /c chcp 65001 | printcd | powershell -Command wt powershell -noexit -command Set-Location -LiteralPath ('"'''+($input -replace ';', '\;' -replace '''', '''''' -replace '%', '==%')+'''.Replace(''==%'',''%'')"')</code> pwshが使えるならprintcdのかわりに<code>pwsh -Command $pwd.Path.Replace('Microsoft.PowerShell.Core\FileSystem::\\', '\\')</code>でもよい。 以下はpiperunを使わずshでやる例。前述の通りパイプからPowerShell側で<code>$input</code>を使って受け取ると文字化けするので引数として渡す。 <code>hiderun "C:\Program Files\Git\usr\bin\sh.exe" -c "WD=$(pwd | /bin/cygpath -w -f - | /bin/sed \"s/;/\\\\\\\\;/g; s/'/''/g; s/%/==%/g\"); wt powershell -noexit -command Set-Location -LiteralPath \(\"'$WD'.Replace('==%','%')\"\)"</code> <span id="git-bashcygwin-1"></span> ==== Git Bash/Cygwin ==== Git Bashは<code>--login</code>をつけてもカレントディレクトリを維持するので単純である。以下をrunotherのtxtに書く。 <code>"C:\Program Files\Git\usr\bin\env.exe" MSYSTEM=MINGW64 /bin/bash --login</code> Cygwinは起動後にcdする。そのかわり<code>export MSYSTEM=MINGW64</code>が不要。 <code>"C:\cygwin64\bin\sh.exe" -c "IFS=;/bin/bash --login -i -c 'cd '$(/bin/printf %q `pwd`)'; exec bash'"</code> printfで一旦エスケープしたものをcdの後につなげてそのまま渡している。 <ul> <li>wtで <ul> <li><p>Git Bash</p> <p><code>wt "C:\Program Files\Git\usr\bin\env.exe" MSYSTEM=MINGW64 "C:\Program Files\Git\usr\bin\bash.exe" --login"</code></p> <ul> <li>あれ、wtなのにカレントディレクトリ維持されてる…?wt cmdではダメなのだが…</li></ul> </li> <li><p>Cygwin</p> <p><code>hiderun "C:\cygwin64\bin\sh.exe" -c "IFS=;WD=$(printf %q `pwd` | /bin/sed \"s/;/\\\\\\\\;/g; s/%/==%/g\"); wt \"C:\cygwin64\bin\bash.exe\" --login -c 'IFS=\\;cd $(echo '$WD'|sed s/==%/%/g)\\; exec bash'"</code></p></li></ul> </li></ul> === 管理者権限あり === <span id="cmd-4"></span> ==== cmd ==== runotherを使って以下の通り。 <code>hiderun cmd /c "chcp 65001 > nul & cd | powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList '-d', ('\"'+$input.TrimEnd('\\').Replace(';','\;')+'\"'), cmd"</code> <code>TrimEnd('\\')</code>のところは<code>C:\</code>の末尾のバックスラッシュへの対応である(<code>wt -d "C:"</code>は通らないので一見ダメそうだが、なぜかこれで動く。原因不明。)。 batならそのまま以下の通り(黒い画面が一瞬出てしまう)。 <code>chcp 65001 & cd | powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList '-d', ('\"'+$input.TrimEnd('\\').Replace(';','\;')+'\"'), cmd</code> 別解としてパイプを使わないものも載せておこう。cmdでは特殊文字ではないので<code>'</code>を<code>''</code>に変えるのは簡単である。 <code>hiderun cmd /c powershell -Command "& {Start-Process -Filepath wt -Verb Runas -ArgumentList '-d', ('\"'+$args[0].TrimEnd('\\').Replace(';','\;')+'\"'), cmd}" -- "'%CD:'=''%'"</code> 以下もうまくいく。cmdの<code>"</code>に関する仕様により、例えばStart-Processの直前に<code>echo '\"';</code>のような<code>"</code>を奇数個含む文字列を入れると動かなくなる。argsを使うとバッククォートがうまくいかなかったのでMyInvocationを使ってみる。 <code>hiderun cmd /c powershell -Command "& {$spl=$MyInvocation.Line.Split('\"'); Start-Process -Filepath wt -Verb Runas -ArgumentList '-d', ('\"'+$spl[$spl.Length-2].TrimEnd('\\').Replace(';','\;')+'\"'), cmd}" -- "\"%CD%\""</code> <span id="powershell-pwsh-1"></span> ==== powershell, pwsh ==== UNC非対応でよく、かつ(<code>``</code>をカレントディレクトリとして起動できる)pwshでよければ、上記のcmdをpwshに変えればよい。powershellならSet-Locationで移動が必要。以下をrunotherのtxtに書く。 <code>hiderun cmd /c "chcp 65001 & cd | powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList 'powershell', '-noexit', '-command', 'Set-Location', '-LiteralPath', ('\"'''+($input -replace '''', '''''' -replace '%', '==%' -replace ';', '\;' )+'''.Replace(''==%'',''%'')\"')"</code> UNC対応なら、管理者でないときと同じで、PSReadLineのエラーを回避するためpwshを一貫して使うのがいいだろう。 <code>hiderun pwsh -Command Start-Process -Verb Runas -Filepath wt -ArgumentList '-d', ('\"'+$pwd.Path.Replace('Microsoft.PowerShell.Core\FileSystem::\\', '\\').TrimEnd('\\').Replace(';','\;')+'\"'), pwsh</code> powershellならpiperunを使う。カレントディレクトリの取得にpwshを使っているがprintcdでもよい。 <code>hiderun piperunex cmd /c chcp 65001 | printcd | powershell -Command Start-Process -Verb Runas -FilePath wt -ArgumentList powershell, -noexit, -command, Set-Location, -LiteralPath, ('\"'''+($input -replace ';', '\;' -replace '''', '''''' -replace '%', '==%')+'''.Replace(''==%'',''%'')\"')</code> shなら以下。 <code>hiderun "C:\Program Files\Git\usr\bin\sh.exe" -c "WD=$( pwd | /bin/cygpath -w -f - | /bin/sed \"s/;/\\\\\\\\;/g; s/'/''''/g; s/%/==%/g\"); powershell -Command Start-Process -Verb Runas -Filepath wt -Argumentlist powershell, -noexit, -command, Set-Location, -LiteralPath, \(\"'\\\"''$WD''.Replace(''==%'',''%'')\\\"'\"\)"</code> <span id="git-bash-2"></span> ==== Git Bash ==== runotherとpiperunで以下。 <code>hiderun piperunex "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=; LANG=en_US.UTF8; pwd || printf %q $(/bin/cat)" | "C:\Program Files\Git\usr\bin\sed.exe" "s/'/''/g;s/%/==%/g;s/;/\\\\;/g" | "C:\Program Files\Git\usr\bin\xargs.exe" -d '\n' -I {} -- powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList '"C:\Program Files\Git\usr\bin\sh.exe"','-c', "'"'"IFS=\\; export MSYSTEM=MINGW64\\;cd $(echo {}|| /bin/sed s/==%/%/g)\\; exec /bin/bash --login"'"'"</code> * <code>MSYSTEM=MINGW64</code>をもっと手前で設定して、<code>-ArgumentList</code>で<code>--login</code>を指定して、最後を<code>exec bash</code>にしてもよい。 piperunを使わずにshで頑張ることもできるが、エスケープが多く読みづらくなる。 <code>hiderun "C:\Program Files\Git\usr\bin\sh.exe" -c "IFS=; LANG=en_US.UTF8; pwd | printf %q $(/bin/cat) | /bin/sed \"s/'/''/g;s/%/==%/g;s/;/\\\\\\\\;/g\" | /bin/xargs -d '\n' -I {} -- powershell -Command Start-Process -Verb Runas -Filepath wt -ArgumentList '\"C:\Program Files\Git\usr\bin\sh.exe\"','-c', \"'\"'\"IFS=\\; export MSYSTEM=MINGW64\\;cd $(echo {}| /bin/sed s/==%/%/g)\\; exec /bin/bash --login\"'\"'\" "</code> Cygwinも同様。 == 関連 == [https://qiita.com/octopath9/items/4b7fe797ae1155c69dd9 コマンドプロンプトから管理者権限のコマンドプロンプトに切り替える - Qiita] [https://qiita.com/tmiki/items/0dab4fd17e9bed04ed91 【Windows】GitBashをcontext menuからAdministrator権限付きで実行する - Qiita] [https://note.com/redpeak/n/n5ddc3673117b エクスプローラーから管理者権限でコマンドプロンプトを開く方法|ひるあんどん] [[Category:IT]][[Category:Windows]]{{#seo:|title={{FULLPAGENAME}} - Turgenev's Wiki}}
Windowsでエクスプローラーからフォルダを各種ターミナルで開く
に戻る。