我想在Visual Studio的C++项目中,作为构建事件之一,使用Windows Subsystem for Linux来运行Bash shell脚本(.sh)。
但是当我这样做时会出现很多错误,我似乎无法找到正确的引号、撇号和反斜杠的组合,以使Bash首先运行,或者正确传递脚本的路径。
我该如何让Visual Studio将我的Bash shell脚本作为构建事件运行?
我想在Visual Studio的C++项目中,作为构建事件之一,使用Windows Subsystem for Linux来运行Bash shell脚本(.sh)。
但是当我这样做时会出现很多错误,我似乎无法找到正确的引号、撇号和反斜杠的组合,以使Bash首先运行,或者正确传递脚本的路径。
我该如何让Visual Studio将我的Bash shell脚本作为构建事件运行?
如果您不关心如何解决问题,只想要一个可以复制粘贴的命令,请随意跳到本答案底部!
我在Visual Studio的构建事件中运行了一些Bash shell脚本,以前我使用Cygwin来运行它们。现在Windows子系统Linux已经可用,我花了一些时间将我的构建迁移到WSL上,但并不像我希望的那么容易,但只要花费一些时间和精力,它是可以工作的。
如果你也想这样做,你会遇到几个问题:
bash.exe
的路径可能不是你想象的那样,因为在内部,Visual Studio使用32位构建过程,因此如果你在64位计算机上运行64位bash.exe
,就会出现恐怖的'C:\Windows\System32\bash.exe' is not recognized
错误。\
)的Windows路径,在Unix中这些符号并不适用,Unix更喜欢将正斜杠(/
)作为路径分隔符。C:\
的无意义符号,在Unix中毫无意义;要在WSL中访问根驱动器,你需要使用/mnt
下的已挂载驱动器。C:\
,而在WSL中它是小写的/mnt/c
。为了让它更加困难,我们不想硬编码任何路径:无论解决方案在哪里找到,它都应该正常工作。
好消息是,所有这些问题都是可以解决的!让我们逐个解决它们。
1. Bash的正确路径
根据这里给出的答案,当从Visual Studio构建中运行Bash时,您需要使用一个特殊的路径来到达Bash。 正确的路径不是C:\Windows\System32\bash.exe
,而实际上是
%windir%\sysnative\bash.exe
神奇的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;'"
C:/Code/MySolution/MyProject/myscript.sh
的内容,这是朝着正确方向迈出的一步。Nmake.exe
、bash
和sed
都会在处理各自的命令行时消耗其中一些特殊符号。C:\
根路径sed
脚本,使其将C:\
变成/mnt/C
。更多的正则表达式替换魔法可以做到这一点。(我们必须在sed
中打开-r
标志,以便我们可以轻松使用捕获组。)%windir%\sysnative\bash.exe -c "echo '$(ProjectDir)myscript.sh'
| sed -re 's/\\\\/\//g; s/([A-Z]):/\/mnt\/\1/i;'"
/mnt/C/Code/MySolution/MyProject/myscript.sh
,这几乎是正确的,但不完全正确。sed
魔法!
\L
命令可用于告诉sed
将后续字符转换为小写(还有一个等效的\U
用于大写)。\E
命令将输出切换回“正常”模式,其中字符保持不变。%windir%\sysnative\bash.exe -c "echo '$(ProjectDir)myscript.sh'
| sed -re 's/\\\\/\//g; s/([A-Z]):/\/mnt\/\L\1\E/i;'"
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;'`"
下面是完整解决方案的样子,用于在当前解决方案的当前项目目录中将名为myscript.sh
的脚本作为构建事件运行:
%windir%\sysnative\bash.exe -c "`echo '$(ProjectDir)myscript.sh' | sed -re 's/\\\\/\//g; s/([A-Z]):/\/mnt\/\L\1\E/i;'`"
它不容易,但并非不可能。
bash
或sh
命令从批处理脚本中调用。使用Windows版的Git,您将在Git\bin
文件夹下找到相关文件,例如:
C:\Program Files\Git\bin
bash.exe
和sh.exe
程序。因此,如果将该目录添加到Windows Path环境变量中,则可以在Windows命令行中使用sh
和bash
。这些命令将允许您在CMD
控制台窗口内“内联”运行bash脚本。也就是说,它们不会生成新的bash窗口;这意味着控制台输出将在您的VS构建中可见。.bat
文件,使用sh
命令或bash
命令调用您的.sh
文件。不确定区别;我们只使用sh
命令。因此,如果您的bash脚本是pre.sh,则批处理文件将只是调用bash脚本的单个命令行。sh %~dp0\pre.sh
if errorlevel 1 (
exit /b %errorlevel%
)
%~dp0
假设批处理和bash脚本在同一个目录中。然后将VS的构建事件指向.bat
文件。检查错误级别是必要的,以便将bash脚本的任何故障转发到批处理脚本中。请参见:如何从Windows命令行获取应用程序退出代码?。.bat
文件的标准说明操作:https://learn.microsoft.com/en-us/visualstudio/ide/specifying-custom-build-events-in-visual-studio?view=vs-2019。
perl
!);但它仍然不是完整的Unix发行版,因此如果您需要其中未包含的任何Unix工具,则需要自己编译它们,或者考虑改用WSL。话虽如此,对于缺乏Unix经验或不愿安装完整的Unix发行版的团队来说,这个解决方案将非常有效,因为安装Git-for-Windows非常容易。 - Sean WerkemaC:\Program Files (x86)\Git\git-bash.exe
,它总是打开一个新窗口,窃取脚本的输出,然后关闭。我没有想到如果我使用 ...\bin\sh
会有所不同。受到你的回答的激励,我尝试了一下,令我惊讶的是,它起作用了。 - Binarus你可以用 $( 和 ) 包裹命令,而不是使用反引号。
<Exec Command="%WINDIR%\sysnative\wsl.exe bash script.sh $(Configuration) $(someOtherArg)" WorkingDirectory="$(MSBuildThisFileDirectory)" ConsoleToMSBuild="true" />
。唯一的问题是从script.sh
访问的文件必须相对于$PWD
。 - cschadl