如何在PowerShell脚本中使用shebang? [英] How can I use a shebang in a PowerShell script?

查看:106
本文介绍了如何在PowerShell脚本中使用shebang?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几个PowerShell脚本,我想直接从Cygwin中的Bash shell作为命令来调用.例如,如果我编写的文件名为 Write-Foo.ps1 的脚本,我想从任何工作目录中将其作为命令执行:

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:

$ Write-Foo.ps1 arg1 arg2 ...

为此,我将脚本添加到PATH中,使其可执行,并在文件开头包括以下解释器shebang/hashbang:

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:

#!/usr/bin/env powershell

Write-Host 'Foo'
...

这是 env 实用程序的常见用法,但它会将脚本与Cygwin的路径前缀(/cygdrive/c/...)分离.至少用于解释器声明.

It's a common (ab)use of the env utility, but it decouples the scripts from Cygwin's path prefix (/cygdrive/c/...), at least for the interpreter declaration.

这可以启动PowerShell,但是系统会将文件作为Cygwin格式的路径传递,PowerShell当然不理解该路径:

This works to start PowerShell, but the system passes the file as a Cygwin-formatted path, which PowerShell doesn't understand, of course:

术语'/cygdrive/c/path/to/Write-Foo.ps1'不被识别为cmdlet,函数,脚本文件或可运行程序的名称.

The term '/cygdrive/c/path/to/Write-Foo.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program.

MSYS(Git Bash)似乎可以正确转换脚本路径,并且只要文件路径不包含空格,脚本就会按预期执行.有没有一种方法可以依靠Cygwin中的shebang直接调用PowerShell脚本?

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?

理想情况下,如果可能的话,我也想从脚本名称中省略 .ps1 扩展名,但我知道我可能需要遵守此限制.如果可能的话,我想避免手动别名或包装脚本.

Ideally, I'd also like to omit the .ps1 extension 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.

快速笔记Linux/macOS用户找到了这个.

推荐答案

Linux/macOS用户发现此内容的简要说明:

  • 确保 pwsh powershell 命令位于PATH
  • 使用以下解释器指令:#!/usr/bin/env pwsh
  • 确保脚本使用Unix样式的行尾(\n不是 \r\n)
  • Ensure the pwsh or powershell command is in PATH
  • Use this interpreter directive: #!/usr/bin/env pwsh
  • Ensure the script uses Unix-style line endings (\n, not \r\n)

感谢 briantist 的评论,我现在知道这不是直接不受6.0之前的PowerShell版本支持:

Thanks to briantist's comments, I now understand that this isn't directly supported for PowerShell versions earlier than 6.0 without compromises:

... [

...[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...

当我们以脚本的形式调用脚本时,类Unix系统会将PowerShell脚本的 absolute 文件名传递给"shebang"指定的解释器作为第一个参数.通常,这有时可以 用于PowerShell 5及以下版本,因为PowerShell默认情况下会将脚本文件名解释为要执行的命令.

A Unix-like system passes the PowerShell script's absolute filename to the interpreter specified by the "shebang" as the first argument when we invoke the script as a command. In general, this can sometimes work for PowerShell 5 and below because PowerShell, by default, interprets the script filename as the command to execute.

但是,我们不能依赖此行为,因为当PowerShell在这种情况下处理-Command时,它 re -解释文件名

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.

相反,当指定-File参数时,PowerShell直接加载脚本,因此我们可以避免使用-Command遇到的问题.不幸的是,要在shebang行中使用此选项,我们需要牺牲使用问题中所述的 env 实用程序所获得的可移植性,因为操作系统程序加载程序通常只允许在其中声明的程序使用一个参数.解释器的脚本.

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 env utility described in the question because operating system program loaders usually allow only one argument to the program declared in the script for the interpreter.

例如,以下解释器指令无效,因为它将两个参数传递给env命令(powershell-File):

For example, the following interpreter directive is invalid because it passes two arguments to the env command (powershell and -File):

#!/usr/bin/env powershell -File

另一方面,在MSYS系统(如Git Bash)中,包含以下指令(具有PowerShell的绝对路径)的PowerShell脚本按预期执行:

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:

#!/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.

这也不能解决Cygwin中的原始问题.如问题所述,脚本本身的路径未转换为Windows样式的路径,因此PowerShell无法找到文件(即使在版本6中也是如此).我想出了一些解决方法,但是都没有提供完美的解决方案.

This also doesn't fix the original problem in Cygwin. As described in the question, the path to the script itself isn'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.

最简单的方法只是利用PowerShell的-Command参数的默认行为.将Write-Foo.ps1脚本添加到环境的命令搜索路径(PATH)之后,我们可以使用脚本名称调用PowerShell,但不使用扩展名:

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 Write-Foo arg1 arg2 ...

只要脚本文件本身在文件名中不包含空格,这便使我们可以从任何工作目录中运行脚本-无需shebang. PowerShell使用本机例程来解析PATH中的命令,因此我们不必担心父目录中的空格.但是,我们失去了Bash的制表符补全命令名称.

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在Cygwin中工作,我需要编写一个代理脚本,该脚本将调用脚本的路径样式转换为PowerShell可以理解的格式.我将其称为 pwsh (用于PS 6的可移植性)并将其放置在PATH:

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:

#!/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样式的路径.如果版本6中的 pwsh.exe 不可用,则此示例将退回到 powershell.exe .然后我们可以在脚本中使用以下解释器指令...

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.exe if pwsh.exe from version 6 isn't available. Then we can use the following interpreter directive in the script...

#!/usr/bin/env pwsh

...并直接调用脚本:

...and invoke a script directly:

$ Write-Foo.ps1 arg1 arg2 ...

对于6.0之前的PowerShell版本,如果我们要创建不带扩展名的原始文件,则可以将该脚本扩展为符号链接或写一个扩展名为 .ps1 的临时PowerShell脚本.

For PowerShell versions before 6.0, the script can be extended to symlink or write out a temporary PowerShell script with a .ps1 extension if we want to create the originals without an extension.

这篇关于如何在PowerShell脚本中使用shebang?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆