如何在 Visual Studio 中将 Bash shell 脚本作为构建事件运行?

Sea*_*ema 5 bash visual-studio windows-subsystem-for-linux

我想.sh在 C++ 项目中使用适用于 Linux 的 Windows 子系统运行 Bash shell 脚本 ( ) 作为 Visual Studio 中构建事件的一部分。

但是当我这样做时有很多错误,我似乎无法找到引号、撇号和反斜杠的正确组合来首先运行 Bash,或者正确地将路径传递给脚本。

如何让 Visual Studio 将我的 Bash shell 脚本作为构建事件运行?

Sea*_*ema 6

(如果您不关心如何解决问题并且只想要一个可以复制和粘贴的命令,请随意跳到此答案的底部!)


概述

我在 Visual Studio 中将许多 Bash shell 脚本作为构建事件的一部分运行,并且我曾经使用 Cygwin 来运行它们。现在适用于 Linux 的 Windows 子系统可用,我花了一些时间将我的构建切换到使用 WSL,这并不像我希望的那么容易,但它可以工作,只需一点时间和精力。

如果你打算这样做,你会遇到几个问题:

  1. 通往的路径bash.exe可能不是你想象的那样,因为在幕后,Visual Studio 使用32 位构建过程,所以如果你在 64 位机器上,你不能简单地运行 64 位bash.exe没有得到可怕的'C:\Windows\System32\bash.exe' is not recognized错误。
  2. 解决方案或项目的路径是使用反斜杠 ( \)的 Windows 路径,而这些在 Unix 中效果不佳,Unix 更喜欢使用正斜杠 ( /) 作为路径分隔符。
  3. 解决方案的根驱动器,通常类似于C:\,在 Unix 中是无意义的胡言乱语;要访问 WSL 中的根驱动器,您需要在/mnt.
  4. Windows 和 WSL 的驱动器号的大小写不同:在 Windows 中,它是大写的C:\,而在 WSL 中,它是小写的/mnt/c

为了让它更难一些,我们不想对任何路径进行硬编码:无论在哪里找到解决方案,它都应该正常工作。

好消息是它们都是可以解决的问题!让我们一次解决它们。


解决问题

1. Bash 的正确路径

根据此处给出的答案,从 Visual Studio 构建运行 Bash 时,您需要使用魔法路径来访问它。正确的路径不是C:\Windows\System32\bash.exe,但实际上是

%windir%\sysnative\bash.exe
Run Code Online (Sandbox Code Playgroud)

魔术sysnative文件夹避免了WOW64层执行的不可见文件系统重定向,并指向本机bash.exe文件。

2. 修复反斜杠

您可能会遇到的下一个问题是反斜杠。理想情况下,您希望运行类似 的项目脚本$(ProjectDir)myscript.sh,但扩展为类似C:\Code\MySolution\MyProject\myscript.sh. 至少,您希望它至少为C:/Code/MySolution/MyProject/myscript.sh,这并不完全正确,但更接近正确。

所以sed来拯救! sed是一种 Unix 工具,可以改变文件中的文本:它使用正则表达式搜索文本,并且可以用修改后的版本替换该文本。在这里,我们将把我们拥有的路径通过管道传输到sed,然后使用一些正则表达式魔法来交换路径分隔符,像这样(为了可读性,这里换行):

%windir%\sysnative\bash.exe -c "echo '$(ProjectDir)myscript.sh'
    | sed -e 's/\\\\/\//g;'"
Run Code Online (Sandbox Code Playgroud)

如果您将此作为构建事件包含在内,您会看到它现在不会运行脚本,但它至少C:/Code/MySolution/MyProject/myscript.sh会向输出控制台打印类似的内容,这是朝着正确方向迈出的一步。

是的,有很多反斜杠、引号和撇号才能正确转义,因为Nmake.exeandbashsed都将在处理各自的命令行时消耗一些特殊符号。

3.修复C:\根路径

我们要变异的sed脚本,以便它把C:\/mnt/C。多一点正则表达式替换魔法可以做到这一点。(而且我们必须打开-r标志,sed以便我们可以轻松使用捕获组。)

%windir%\sysnative\bash.exe -c "echo '$(ProjectDir)myscript.sh'
    | sed -re 's/\\\\/\//g; s/([A-Z]):/\/mnt\/\1/i;'"
Run Code Online (Sandbox Code Playgroud)

如果您运行它,您现在将看到输出路径类似于/mnt/C/Code/MySolution/MyProject/myscript.sh,这几乎但不完全正确。

4. 修复根路径中的case-change

WSL 以小写形式安装您的磁盘,而 Windows 以大写形式安装它们。一致性!我们如何解决这个问题还有更多的sed魔法!

\L命令可用于告诉sed将后续字符转换为小写(并且有\U大写的等效项)。该\E命令将输出切换回“正常”模式,其中字符保持不变。

在 finally 中添加这些会导致输出正确的路径:

%windir%\sysnative\bash.exe -c "echo '$(ProjectDir)myscript.sh'
    | sed -re 's/\\\\/\//g; s/([A-Z]):/\/mnt\/\L\1\E/i;'"
Run Code Online (Sandbox Code Playgroud)

5. 运行

一直以来,Bash 一直在打印脚本的路径。既然它是正确的路径,我们该如何运行它呢?

答案是添加`反引号`!反引号使 Bash 执行其中包含的命令,然后将该命令的输出用作下一个命令的参数。在这种情况下,我们不会输出任何内容:我们只想将输出sed作为命令运行。

所以包括反引号,结果如下:

%windir%\sysnative\bash.exe -c "`echo '$(ProjectDir)myscript.sh'
    | sed -re 's/\\\\/\//g; s/([A-Z]):/\/mnt\/\L\1\E/i;'`"
Run Code Online (Sandbox Code Playgroud)

完整的解决方案

这是整个解决方案的外观,用于myscript.sh在当前解决方案的当前项目目录中运行名为构建事件的脚本:

%windir%\sysnative\bash.exe -c "`echo '$(ProjectDir)myscript.sh' | sed -re 's/\\\\/\//g; s/([A-Z]):/\/mnt\/\L\1\E/i;'`"
Run Code Online (Sandbox Code Playgroud)

这是一个屏幕截图,显示了 Visual Studio 2017 中真实 C++ 项目的相同内容:

在此处输入图片说明

这并不容易,但并非不可能。