如何在PowerShell上使用WSL运行Bash脚本?

9

在我的Windows当前目录下,我有以下脚本文件simple_script.sh:

#!/bin/bash
echo "hi from simple script"

我希望能够通过PowerShell命令行在WSL上运行此脚本。

使用wsl命令,我找不到一种方法来告诉wsl调用脚本代码。

以下命令可以工作(我想)

wsl bash -c "echo hi from simple script"

然而,当尝试将脚本内容加载到变量中并运行时,结果并不如预期:
$simple_script = Get-Content ./simple_script.sh
wsl bash -c $simple_script

执行失败:

bash: -c: option requires an argument

我尝试了几种变体。 使用带有-Raw标志的Get-Content似乎会使字符串中的第一个单词打印出来(但不是整个字符串)。 不包含“"”字符的命令有时候可以工作。 但我没有找到一种一致的方法。 一个类似的问题似乎无法直接与wsl一起使用,并且似乎无法运行驻留在Windows文件系统上的脚本文件。
3个回答

20

在 Windows 中以韵律行为基础的 shell 脚本中执行的强大高效的方式是通过wsl.exe -e

wsl -e ./simple_script.sh # !! ./ is required

注意:

  • 如果没有使用./来明确指示可执行文件位于当前目录中,则该命令将会悄无声息地失败(只有在PATH环境变量所列出的目录中才可以通过名称调用可执行文件)。

  • -e绕过了Linux端的shell,并使Linux系统函数解释基于shebang行的纯文本文件,该行自动支持指定解释器。

    • 令人惊讶的是,WSL认为所有位于Windows文件系统中的文件,包括纯文本文件,在它们的可执行位上都被设置,你可以使用wsl -e ls -l ./simple_script.sh轻松验证此点。

关于你尝试过的内容
$simple_script = Get-Content ./simple_script.sh
wsl bash -c $simple_script

主要问题在于 Get-Content 默认会返回一个行数组,而试图将该数组作为参数传递给外部程序(例如wsl) 将使其每个元素被传递为单独的参数

即时解决方案是使用-Raw开关,它使Get-Content将文件内容作为一个单一、多行字符串返回

然而,由于一个非常不幸的长期bug,PowerShell - 直到v7.2.x - 需要 手动 \-转义在传递给外部程序的参数中嵌入的"字符

因此:

# Using -Raw, read the file in full, as a single, multi-line string.
$simple_script = Get-Content -Raw ./simple_script.sh

# !! The \-escaping is needed up to PowerShell 7.2.x
wsl bash -c ($simple_script -replace '"', '\"')

请注意,尽管通过管道(stdin)提供脚本文本是一种诱人的绕过转义需求的方法,但这在PowerShell 7.3.3中不会按预期工作
# !! Tempting, but does NOT work.
Get-Content -Raw ./simple_script.sh | wsl -e bash

这个不起作用的原因是PowerShell通过管道发送给外部程序的内容,总是会附加一个Windows格式的换行符(CRLF),而Bash无法识别。


3
哇塞!!!!!!你为此成为了传奇。如果你需要什么,我可以给你,知道吗...欠你一个。 - SMTP King
这太棒了。一旦shell脚本完成,有没有办法获取其输出?我有一个脚本会输出一系列HTML颜色的十六进制代码,我需要在我的PowerShell脚本中进一步使用它们。 - undefined
还有,有没有办法以这种方式传递参数给shell脚本?我需要将一个图像文件传递给脚本。 - undefined
@fmotion1,只需将参数附加到命令行。在PowerShell中,捕获输出与任何命令一样,例如$output = wsl -e ./simple_script.sh arg1 arg2,但请注意,这仅涵盖成功流/标准输出。要同时捕获标准错误输出,请使用2>重定向。 - undefined

2

我曾经遇到过以上步骤无法正常工作的问题。原来是由于WSL中默认的Linux发行版存在问题。以下步骤解决了这个问题。

步骤1:检查当前发行版

 wsl --list
Windows Subsystem for Linux Distributions:
docker-desktop-data (Default)
Ubuntu
docker-desktop

似乎 docker-desktop-data 是问题的根源。

步骤2:更改默认值

> wsl --setdefault Ubuntu

之后一切都开始正常工作了。

另请参阅
如何在 Windows 10 上使用 WSL2 设置默认发行版


1
啊,又是 docker-desktop-data 的问题。在这种情况下,我也鼓励您投票支持 此问题 和/或 (微软) 在 此评论 中提出的解决方案。我希望看到这个实现,这样人们将来就不会遇到这个特定的问题了。 - NotTheDr01ds
2
还要注意,您可以使用 wsl -d Ubuntu -e ./script.sh任何已安装的发行版中运行脚本(如果需要,替换实际发行版名称以代替 Ubuntu)。 - NotTheDr01ds

0

要在 wsl 上运行脚本,只需调用 bash

> bash simple_script.sh
hi from simple script

如果要将其保存在变量中,并作为 wslpowershell 中的 bash 脚本运行,则不需要使用 Get-Content

> $simple_script = bash /mnt/c/Users/user-name/path/to/simple_script.sh
> Write-Output $simple_script
hi from simple script

注意: Powershell将echo映射为Write-Output的别名,因此您也可以使用echo

> $simple_script = bash /mnt/c/Users/user-name/path/to/simple_script.sh
> echo $simple_script
hi from simple script

如果你最初的目的是获取内容,你也可以抓取它。

> Get-Content simple_script.sh
#!/bin/bash
echo "hi from simple script"
 
> $content = Get-Content .\simple_script.sh
> Write-Output $content
#!/bin/bash
echo "hi from simple script"


我的错误在于尝试使用 bash ./simple_script.sh 而不是只用 bash simple_script.sh。这很令人困惑。 - Elad Maimoni
1
提醒一下,在WSL中,bash命令已被弃用。建议改用wsl命令,它更加灵活。 - NotTheDr01ds
@EladMaimoni,bash ./simple_script.sh 也可以工作,并且实际上更可取,因为与 bash simple_script.sh 不同,它不会回退到在 PATH 中查找 simple_script.sh 脚本(如果它不在当前目录中)。但是,除非您确定相同的 shell 也是在 shebang 行中指定的 shell,否则通常不应将带有 shebang 行的 shell 脚本传递给特定的 shell 进行直接执行。简而言之:请改用 wsl -e ./simple_script.sh - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接