web-dev-qa-db-ja.com

PowerShellスクリプトでシバンを使用するにはどうすればよいですか?

CygwinのBashシェルからコマンドとして直接呼び出すPowerShellスクリプトがいくつかあります。たとえば、ファイル名を指定してスクリプトを記述した場合 Write-Foo.ps1、任意の作業ディレクトリからコマンドとして実行したい:

$ Write-Foo.ps1 arg1 arg2 ...

これを行うには、スクリプトをPATHに追加して実行可能にし、次のインタープリターShebang/hashbangをファイルの先頭に含めます。

#!/usr/bin/env powershell

Write-Host 'Foo'
...

これは一般的な(乱用)使用法です 環境 ユーティリティですが、Cygwinのパスプレフィックス(/ cygdrive/c/...)、少なくともインタープリター宣言では。

これはPowerShellを起動するために機能しますが、システムはファイルをCygwin形式のパスとして渡しますが、PowerShellはこれを理解できません。

「/cygdrive/c/path/to/Write-Foo.ps1」という用語は、コマンドレット、関数、スクリプトファイル、または操作可能なプログラムの名前として認識されません。

MSYS(Git Bash)はスクリプトパスを正しく変換するようで、ファイルへのパスにスペースが含まれていない限り、スクリプトは期待どおりに実行されます。 Cygwinのシバンに依存してPowerShellスクリプトを直接呼び出す方法はありますか?

理想的には、私も省略したいと思います .ps1 可能であればスクリプト名からの拡張子ですが、この制限に耐える必要があることを理解しています。可能であれば、スクリプトのエイリアスを手動で付けたり、ラップしたりしないようにします。


これを見つけたLinux/macOSユーザーのための簡単なメモ

12
Cy Rossignol

これを見つけたLinux/macOSユーザーのためのクイックノート:

  • pwshまたはpowershellコマンドがPATHにあることを確認します
  • 次のインタープリターディレクティブを使用してください:#!/usr/bin/env pwsh
  • スクリプトがUnixスタイルの行末を使用していることを確認します(\nnot\r\n

briantist のコメントのおかげで、6.0より前のバージョンのPowerShellでは妥協なしにこれが直接でサポートされていないことがわかりました。

... [ PowerShell Core 6.0の場合 ]そのため、位置パラメーター0を‑Commandから‑Fileに変更しました。 ...表示されるエラーメッセージは、‑Command...へのパスを渡しているためです.

Unixライクなシステムは、PowerShellスクリプトのabsoluteファイル名を、スクリプトをコマンドとして呼び出すときに、最初の引数として「Shebang」で指定されたインタープリターに渡します。一般に、PowerShellはデフォルトでスクリプトファイル名を実行するコマンドとして解釈するため、これはPowerShell 5以下でときどき機能します。

ただし、PowerShellがこのコンテキストで-Commandを処理すると、re-ファイル名を解釈するため、この動作に依存することはできません プロンプト なので、スペースまたは特定の記号を含むスクリプトのパスは、PowerShellが引数として認識する「コマンド」を壊します。また、予備的な解釈手順の効率も少し低下します。

代わりに-Fileパラメーターを指定すると、PowerShellがスクリプトを直接ロードするため、-Commandで発生する問題を回避できます。残念ながら、シバン行でこのオプションを使用するには、以下を使用して得られる移植性を犠牲にする必要があります。 環境 オペレーティングシステムのプログラムローダーは通常、インタープリターのスクリプトで宣言されたプログラムへの引数を1つだけ許可するため、質問で説明されているユーティリティ。

たとえば、次のインタープリターディレクティブは、2つの引数をenvコマンド(powershellおよび-File)に渡すため無効です。

#!/usr/bin/env powershell -File

一方、MSYSシステム(Git Bashなど)では、次のディレクティブ(PowerShellへの絶対パスを含む)を含むPowerShellスクリプトが期待どおりに実行されます。

#!/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -File

...しかし、同じファイルシステムの規則に従わない別のシステムでスクリプトを直接実行することはできません。


これはCygwinの元の問題も修正しません。質問で説明したように、スクリプト自体へのパスはWindowsスタイルのパスに変換されないため、PowerShellはファイルを見つけることができません(バージョン6でも)。私はいくつかの回避策を見つけましたが、どちらも完璧な解決策を提供しません。

最も単純なアプローチは、PowerShellの-Commandパラメーターのデフォルトの動作を利用するだけです。 Write-Foo.ps1スクリプトを環境のコマンド検索パス(PATH)に追加した後、スクリプト名を使用してPowerShellを呼び出し、拡張子を削除できます。

$ powershell Write-Foo arg1 arg2 ...

スクリプトファイル自体のファイル名にスペースが含まれていない限り、これにより、任意の作業ディレクトリからスクリプトを実行できます。Shebangは必要ありません。 PowerShellはネイティブルーチンを使用してPATHからのコマンドを解決するため、親ディレクトリ内のスペースについて心配する必要はありません。ただし、コマンド名のBashのタブ補完は失われます。

ShebangをCygwinで動作させるには、呼び出されたスクリプトのパススタイルをPowerShellが理解できる形式に変換するプロキシスクリプトを記述する必要がありました。私はそれを呼んだ pwsh (PS 6との移植性のため)PATHに配置:

#!/bin/sh

if [ ! -f "$1" ]; then 
    exec "$(command -v pwsh.exe || command -v powershell.exe)" "$@"
    exit $?
fi

script="$(cygpath -w "$1")"
shift

if command -v pwsh.exe > /dev/null; then 
    exec pwsh.exe "$script" "$@"
else
    exec powershell.exe -File "$script" "$@"
fi

スクリプトは最初の引数をチェックすることから始まります。ファイルでない場合は、通常どおりPowerShellを起動します。それ以外の場合、スクリプトはファイル名をWindowsスタイルのパスに変換します。この例は、 powershell.exe もし pwsh.exe バージョン6以降は使用できません。次に、スクリプトで次のインタープリターディレクティブを使用できます...

#!/usr/bin/env pwsh

...そしてスクリプトを直接呼び出す:

$ Write-Foo.ps1 arg1 arg2 ...

6.0より前のバージョンのPowerShellの場合、スクリプトを拡張してシンボリックリンクにしたり、一時的なPowerShellスクリプトを .ps1 拡張子なしでオリジナルを作成する場合は、拡張子。

11
Cy Rossignol