【问题标题】:How can I use a shebang in a PowerShell script?如何在 PowerShell 脚本中使用 shebang?
【发布时间】:2018-01-11 21:35:16
【问题描述】:

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

$ Write-Foo.ps1 arg1 arg2 ...

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

#!/usr/bin/env powershell

Write-Host 'Foo'
...

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

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

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

MSYS (Git Bash) 似乎可以正确转换脚本路径,只要文件路径不包含空格,脚本就会按预期执行。有没有办法通过 Cygwin 中的 shebang 直接调用 PowerShell 脚本?

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


A quick note for Linux/macOS users finding this.

【问题讨论】:

  • 你试过PowerShell Core 6.0 by any chance? It was just released yesterday。他们专门将位置参数 0 从 -Command 更改为 -File 以使其工作。
  • 是的,直到 PS 6 才真正提供 shebang 支持,这实际上距离发布不到一周。鉴于 PS 6(和 .Net Core)缺乏的东西,我仍然不会称其为生产就绪。我认为这是“不是现在,而是最终”。
  • @briantist 哦啦啦...看起来很有趣。我还没有尝试过...需要一些时间来玩。不过,我希望 v6 不会解决这个特定问题。我觉得这是 Cygwin 环境固有的挑战,可能需要在控制到达 PowerShell 之前处理。
  • @CyRossignol 我不能肯定地说新版本会解决这个问题,但是除了以 shebang 为中心的变化之外,它在 Linux/MacOS 上得到了支持,因此它对路径的理解和支持是更新以支持这些平台。但实际上,您收到的错误消息,因为它传递了一条到-Command 的路径,这不起作用。也许您可以将powershell 别名为隐式调用powershell -File?我对 bash 别名的了解不够多,不知道在这种情况下这是否可行或是否可行。
  • @briantist 谢谢,很好的信息和建议。该别名将起作用,但我仍然需要将路径传递给脚本,我希望避免这种情况。有趣的是,我刚试了powershell Write-Foo,就达到了这个目的。因为我将该脚本放入环境的 PATH 中,所以 PS 似乎找到了它并接受该脚本作为有效命令(即使没有扩展名)。我仍然想让shebang工作,但这种方式已经好多了。

标签: bash shell powershell cygwin shebang


【解决方案1】:

发现此问题的 Linux/macOS 用户的快速说明:

  • 确保 pwshpowershell 命令在 PATH
  • 使用此解释器指令:#!/usr/bin/env pwsh
  • 确保脚本使用 Unix 风格的行尾(\n不是\r\n

感谢briantist 的 cmets,我现在明白了 6.0 之前的 PowerShell 版本不直接不妥协地支持:

...[in PowerShell Core 6.0] 他们专门将位置参数 0 从 ‑Command 更改为 ‑File 以使其工作。 ...您收到的错误消息是因为它正在将路径传递给‑Command...

当我们将脚本作为命令调用时,类 Unix 系统将 PowerShell 脚本的 absolute 文件名传递给由“shebang”指定的解释器作为第一个参数。通常,这有时适用于 PowerShell 5 及更低版本,因为默认情况下,PowerShell 将脚本文件名解释为要执行的命令。

但是,我们不能依赖这种行为,因为当 PowerShell 在此上下文中处理 -Command 时,它重新-解释文件名 as if it was typed at the prompt,因此脚本的路径包含空格或某些符号将破坏 PowerShell 视为参数的“命令”。我们在初步解释步骤中也损失了一点效率。

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

例如,以下解释器指令无效,因为它将两个参数传递给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 样式的路径。如果版本 6 中的 pwsh.exe 不可用,则此示例回退到 powershell.exe。然后我们可以在脚本中使用下面的解释器指令...

#!/usr/bin/env pwsh

...直接调用脚本:

$ Write-Foo.ps1 arg1 arg2 ...

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-21
    • 2020-02-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 2012-05-11
    • 2011-09-20
    • 2016-10-16
    相关资源
    最近更新 更多