bash 如何在 PowerShell 脚本中使用 shebang?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48216173/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I use a shebang in a PowerShell script?
提问by Cy Rossignol
I have several PowerShell scripts that I'd like to invoke directly as a command from a Bash shell in Cygwin. For example, if I write a script with the filename Write-Foo.ps1, I'd like to execute it as a command from any working directory:
我有几个 PowerShell 脚本,我想直接从 Cygwin 中的 Bash shell 作为命令调用它们。例如,如果我编写一个文件名为 Write-Foo.ps1的脚本,我想将它作为来自任何工作目录的命令执行:
$ Write-Foo.ps1 arg1 arg2 ...
To do this, I add the script to my PATH, make it executable, and include the following interpreter shebang/hashbang at the beginning of the file:
为此,我将脚本添加到我的 PATH 中,使其可执行,并在文件开头包含以下解释器 shebang/hashbang:
#!/usr/bin/env powershell
Write-Host 'Foo'
...
It's a common (ab)use of the envutility, but it decouples the scripts from Cygwin's path prefix (/cygdrive/c/...), at least for the interpreter declaration.
这是env实用程序的常见 (ab) 使用,但它将脚本与 Cygwin 的路径前缀 ( /cygdrive/c/...) 分离,至少对于解释器声明而言。
This works to start PowerShell, but the system passes the file as a Cygwin-formatted path, which PowerShell doesn't understand, of course:
这适用于启动 PowerShell,但系统将文件作为 Cygwin 格式的路径传递,PowerShell 不理解,当然:
The term '/cygdrive/c/path/to/Write-Foo.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program.
术语“/cygdrive/c/path/to/Write-Foo.ps1”不被识别为 cmdlet、函数、脚本文件或可运行程序的名称。
MSYS (Git Bash) seems to translate the script path correctly, and the script executes as expected, as long as the path to the file contains no spaces. Is there a way to invoke a PowerShell script directly by relying on the shebang in Cygwin?
MSYS (Git Bash) 似乎正确地转换了脚本路径,只要文件路径不包含空格,脚本就会按预期执行。有没有办法通过依赖 Cygwin 中的 shebang 直接调用 PowerShell 脚本?
Ideally, I'd also like to omit the .ps1extension from the script names if possible, but I understand that I may need to live with this limitation. I want to avoid manually aliasing or wrapping the scripts if possible.
理想情况下,如果可能的话,我还想从脚本名称中省略.ps1扩展名,但我知道我可能需要忍受这个限制。如果可能,我想避免手动别名或包装脚本。
回答by Cy Rossignol
Quick note for Linux/macOS users finding this:
Linux/macOS 用户的快速说明:
- Ensure the pwshor powershellcommand is in
PATH
- Use this interpreter directive:
#!/usr/bin/env pwsh
- Ensure the script uses Unix-style line endings (
\n
, not\r\n
)
- 确保pwsh或powershell命令在
PATH
- 使用这个解释器指令:
#!/usr/bin/env pwsh
- 确保脚本使用 Unix 风格的行尾(
\n
, not\r\n
)
Thanks to briantist's comments, I now understand that this isn't directlysupported for PowerShell versions earlier than 6.0 without compromises:
感谢briantist的评论,我现在明白PowerShell 6.0 之前的版本不直接支持而没有妥协:
...[in PowerShell Core 6.0] they specifically changed positional parameter 0 from
‑Command
to‑File
to make that work. ...the error message you're getting is because it's passing a path to‑Command
...
...[在 PowerShell Core 6.0 中] 他们专门将位置参数 0 从 更改
‑Command
为‑File
以使其工作。...您收到的错误消息是因为它正在将路径传递给‑Command
...
A Unix-like system passes the PowerShell script's absolutefilename to the interpreter specified by the "shebang" as the first argument when we invoke the script as a command. In general, this can sometimeswork for PowerShell 5 and below because PowerShell, by default, interprets the script filename as the command to execute.
当我们将脚本作为命令调用时,类 Unix 系统将 PowerShell 脚本的绝对文件名传递给“shebang”指定的解释器作为第一个参数。通常,这有时适用于 PowerShell 5 及以下版本,因为 PowerShell 默认将脚本文件名解释为要执行的命令。
However, we cannot rely on this behavior because when PowerShell's handles -Command
in this context, it re-interprets the filename as if it was typed at the prompt, so the path of a script that contains spaces or certain symbols will break the "command" that PowerShell sees as the argument. We also lose a bit of efficiency for the preliminary interpretation step.
但是,我们不能依赖这种行为,因为当 PowerShell-Command
在此上下文中处理 时,它会重新解释文件名,就好像它是在提示符下键入的一样,因此包含空格或某些符号的脚本的路径将破坏“命令” PowerShell 将其视为参数。我们也失去了初步解释步骤的一些效率。
When specifying the -File
parameter instead, PowerShell loads the script directly, so we can avoid the problems we experience with -Command
. Unfortunately, to use this option in the shebang line, we need to sacrifice the portability we gain by using the envutility described in the question because operating system program loaders usually allow only one argument to the program declared in the script for the interpreter.
当-File
改为指定参数时,PowerShell 会直接加载脚本,因此我们可以避免使用-Command
. 不幸的是,要在 shebang 行中使用此选项,我们需要牺牲通过使用问题中描述的env实用程序获得的可移植性,因为操作系统程序加载器通常只允许对在解释器脚本中声明的程序使用一个参数。
For example, the following interpreter directive is invalid because it passes two arguments to the env
command (powershell
and -File
):
例如,以下解释器指令无效,因为它向env
命令 (powershell
和-File
)传递了两个参数:
#!/usr/bin/env powershell -File
In an MSYS system (like Git Bash), on the other hand, a PowerShell script that contains the following directive (with the absolute path to PowerShell) executes as expected:
另一方面,在 MSYS 系统(如 Git Bash)中,包含以下指令(带有 PowerShell 的绝对路径)的 PowerShell 脚本按预期执行:
#!/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -File
...but we cannot directly execute the script on another system that doesn't follow the same filesystem convention.
...但我们不能直接在另一个不遵循相同文件系统约定的系统上执行脚本。
This also doesn't fix the original problem in Cygwin. As described in the question, the path to the script itselfisn't translated to a Windows-style path, so PowerShell cannot locate the file (even in version 6). I figured out a couple of workarounds, but neither provide a perfect solution.
这也不能解决 Cygwin 中的原始问题。如问题中所述,脚本本身的路径未转换为 Windows 样式的路径,因此 PowerShell 无法找到该文件(即使在版本 6 中)。我想出了几个解决方法,但都没有提供完美的解决方案。
The simplest approach just exploits the default behavior of PowerShell's -Command
parameter. After adding the Write-Foo.ps1
script to the environment's command search path (PATH
), we can invoke PowerShell with the script name, sans the extension:
最简单的方法就是利用 PowerShell-Command
参数的默认行为。将Write-Foo.ps1
脚本添加到环境的命令搜索路径 ( PATH
) 后,我们可以使用脚本名称调用 PowerShell,无需扩展名:
$ powershell Write-Foo arg1 arg2 ...
As long as the script file itself doesn't contain spaces in the filename, this allows us to run the script from any working directory—no shebang needed. PowerShell uses a native routine to resolve the command from the PATH
, so we don't need to worry about spaces in the parent directories. We lose Bash's tab-completion for the command name, though.
只要脚本文件本身的文件名中不包含空格,这允许我们从任何工作目录运行脚本——不需要shebang。PowerShell 使用本机例程来解析来自 的命令PATH
,因此我们无需担心父目录中的空格。但是,我们丢失了 Bash 对命令名称的制表符补全。
To get the shebang to work in Cygwin, I needed to write a proxy script that converts the path style of the invoked script to a format that PowerShell understands. I called it pwsh(for portability with PS 6) and placed it in the PATH
:
为了让 Shebang 在 Cygwin 中工作,我需要编写一个代理脚本,将调用脚本的路径样式转换为 PowerShell 可以理解的格式。我称它为pwsh(为了与 PS 6 的便携性)并将它放在PATH
:
#!/bin/sh
if [ ! -f "" ]; then
exec "$(command -v pwsh.exe || command -v powershell.exe)" "$@"
exit $?
fi
script="$(cygpath -w "")"
shift
if command -v pwsh.exe > /dev/null; then
exec pwsh.exe "$script" "$@"
else
exec powershell.exe -File "$script" "$@"
fi
The script begins by checking the first argument. If it isn't a file, we just start PowerShell normally. Otherwise, the script translates the filename to a Windows-style path. This example falls back to powershell.exeif pwsh.exefrom version 6 isn't available. Then we can use the following interpreter directive in the script...
该脚本首先检查第一个参数。如果不是文件,我们就正常启动PowerShell。否则,脚本会将文件名转换为 Windows 样式的路径。如果版本 6 中的pwsh.exe不可用,则此示例将回退到powershell.exe。然后我们可以在脚本中使用以下解释器指令...
#!/usr/bin/env pwsh
...and invoke a script directly:
...并直接调用脚本:
$ Write-Foo.ps1 arg1 arg2 ...
For PowerShell versions before 6.0, the script can be extended to symlink or write out a temporary PowerShell script with a .ps1extension if we want to create the originals without an extension.
对于 6.0 之前的 PowerShell 版本,如果我们想创建没有扩展名的原始脚本,可以将脚本扩展为符号链接或写出带有.ps1扩展名的临时 PowerShell 脚本。