如何在PowerShell脚本中使用shebang?

时间:2018-01-11 21:35:17

标签: bash shell powershell cygwin shebang

我有几个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.

1 个答案:

答案 0 :(得分:9)

Linux / macOS用户发现这一点的快速说明:

  • 确保 pwsh powershell 命令位于PATH
  • 使用此解释器指令:#!/usr/bin/env pwsh
  • 确保脚本使用Unix样式的行结尾(\n \r\n

感谢briantist的评论,我现在明白,对于早于6.0的PowerShell版本,这不是直接而不妥协:

  

... [in PowerShell Core 6.0]他们专门将位置参数0从‑Command更改为‑File以使其有效。 ...你得到的错误信息是因为它传递了‑Command ...

的路径

当我们将脚本作为命令调用时,类似Unix的系统将PowerShell脚本的绝对文件名传递给由“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的tab-completion。

为了让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脚本。