How to escape spaces the parameters to a PS script running in VS post-build event
Asked Answered
A

4

8

Update 1:

After looking carefully at the output again, I sort of figured it out. By adding a trailing space between the closing parenthesis and the quotes it works:

powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" "$(ProjectDir) " "$(TargetDir) "

I am suspecting that PowerShell somehow interprets the )".

Is there a more elegant way around this issue?

Update 2:

This is weird. I have another script that does the clean up and works with out the space between ) and " like this:

powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersionCleanup.ps1" "$(ProjectDir)"

However adding trailing spaces will cause it to fail, because internally it appends a file-name to the path, so that the path will be incorrect.

If any one understand this, I would be happy to accept the explanation as the correct answer!


Original:

I have the following prebuild-command in VisualStudio, which I want to use to inject the version from a Git-tag:

powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" $(ProjectDir) $(TargetDir)

This works fine, if the path $(SolutionDir) does not contain spaces (which will then also present in $(ProjectDir) or $(TargetDir)).

When the path $(SolutionDir) does contain spaces, it appears the script starts as expected, but the arguments are not passed correctly and I am unable to figure out how to escape them in the arguments to the PS-script.

I have tried adding sing ", triple """ and also ', which gives the following (each PS-command tries a different method for escaping the spaces):

powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" $(ProjectDir) $(TargetDir)

args[0]:

D:\VisualStudio

args[1]:

Projects\software_git_repo\ProgramEditor\

powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" "$(ProjectDir)" "$(TargetDir)"

BS: args[0]:

D:\VisualStudio Projects\software_git_repo\ProgramEditor" D:\VisualStudio

BS: args[1]:

Projects\software_git_repo\ProgramEditor\bin\Debug"

powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" """$(ProjectDir)""" """$(TargetDir)"""

BS: args[0]:

"D:\VisualStudio

BS: args[1]:

Projects\software_git_repo\ProgramEditor"

powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" '$(ProjectDir)' '$(TargetDir)'  

BS: args[0]:

'D:\VisualStudio

BS: args[1]:

Projects\software_git_repo\ProgramEditor\'

Doing:

echo ProjectDir:
echo $(ProjectDir)
echo TargetDir:
echo $(TargetDir)

I get:

ProjectDir:

D:\VisualStudio Projects\software_git_repo\ProgramEditor\

TargetDir:

D:\VisualStudio Projects\software_git_repo\ProgramEditor\bin\Debug\

Aliaalias answered 6/9, 2018 at 13:22 Comment(1)
@(...) is a array subexpression operator for PowerShell, "it returns the result of one or more statements as an array. If there is only one item, the array has only one member". I guess this is a duplicate with: Post-build powershell script. Solution: use single quoted named parameters.Verdie
T
7

Beware unexpected escaped quotes when a VS directory macro is expanded in a quoted parameter.


A script parameter containing a path should be wrapped in quotes to avoid being interpreted as multiple parameters if the path includes spaces. The following would therefore seem reasonable in Visual Studio's pre/post build event command line:-

powershell.exe -file "$(SolutionDir)script.ps1" -projectDirectory "$(ProjectDir)"

But the macro values that specify directories always include a trailing backslash; so when they're expanded the line becomes, for example:-

powershell.exe -file "C:\sln path\script.ps1" -projectDirectory "C:\sln path\project\"

...where the trailing \" is an escaped character that is interpreted as part of the parameter value, which is passed to the script as:-

C:\sln path\project"

Solution

The fix in my case was to insert an additional backslash after the directory macro to be escaped instead of the quotes, which restores the expected parameter value and prevents the mangling of any subsequent params:-

powershell.exe -file "$(SolutionDir)script.ps1" -projectDirectory "$(ProjectDir)\"

Not very intuitive though. Let me know if I'm missing something more obvious...

Textualism answered 30/9, 2020 at 22:58 Comment(0)
C
1

thank you @tranquil_tarn for your great answer! I struggled with the BATCH + Powershell escaping hell for multiple hours. Posting final code here to hopefully help someone out.

Solution

@ECHO OFF
cls
echo .SYNOPSIS
echo    Allows prebuild to be Powershell, instead of BATCH. (You'll thank me later)
echo    Specifically this example shows passing in VisualStudio (ProjectDir) variable AND correctly handles spaces in the path.
echo .DESCRIPTION
echo    Call prebuild.ps1 and pass in built-in VS Variables: https://learn.microsoft.com/en-us/visualstudio/ide/reference/pre-build-event-post-build-event-command-line-dialog-box?view=vs-2022
echo    Use powershell best-practices named parameters rather than positional to avoid positional-unmatched errors.
echo    If Username '$(User)' is blank DO NOT include the Switch at all to avoid powershell errors


REM TASK1: Check a variable for blank/empty and only include the Param if not empty
SET "user=$(User)"
IF [%user%]==[] (
    echo "userParam is blank"
) ELSE (
    SET "userParam=-User:%user%"
)
echo "userParam: %userParam%" 

REM TASK2: Warning: (ProjectDir) in VisualStudio ends in a slash, which when expanded, results in escaped quote \' at end of line
REM if you DO NOT include the quotes, then spaces in path will break it.  if you DO include quotes then it gets escaped.
REM solution is to include an extra slash at the end, resulting in double backslash \\ which escapes down to single backslash '\'... whew! 
REM 'see https://mcmap.net/q/1336393/-how-to-escape-spaces-the-parameters-to-a-ps-script-running-in-vs-post-build-event/64146880#64146880'
cd "$(ProjectDir)\"
SET cmd1=powershell -NoProfile -ExecutionPolicy Unrestricted -Command ^
    ".\prebuild.ps1 -ProjectDir:'$(ProjectDir)\' -Conf:'$(Configuration)' -ProjectName:'$(ProjectName)' %userParam% "
REM For debugging, view the command (including escaped strings) before calling it.
echo %cmd1%
call %cmd1%
Captivity answered 18/8, 2022 at 20:30 Comment(0)
R
0

One thing I noticed is an extra backslash after $(SolutionDir). I'm using VS2017 and I think consistently $(SolutionDir) has always had a backslash. Not sure though.

I have a .bat in my solution folder that creates a json config file. My Post-Build event looks like this.

call "$(SolutionDir)CreateConfigurationJson.bat" "$(TargetDir)mySettings.json"
{"ProjectName":"$(ProjectName)"}

"$(TargetDir)mySettings.json" is the first parameter of the .bat file

{"ProjectName":"$(ProjectName)"} is the second parameter.

No escape characters necessary. Hope this helps.

The code for the bat file is:

echo off
set filePath=%1
break>%filePath%
set json=%2
echo %json% >>%filePath%
Ressler answered 5/2, 2019 at 2:52 Comment(0)
P
0

I got tangled up with spaces in my Project Directory. I was trying to edit the Target element in the .csproj file of the project. The trailing quote was being escaped but adding a space directly in the .csproj did not work. I had to add the trailing space under Project Properties > Build > Events:

enter image description here

Which encoded the quotes under the Target element in the .csproj:

  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="powershell.exe -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -File &quot;$(ProjectDir)post-build.ps1&quot; -ProjectDir &quot;$(ProjectDir) &quot; -ProjectPath &quot;$(ProjectPath) &quot;" />
  </Target>
Predate answered 29/6 at 13:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.