4

I'm using PowerShell v3.0 to start a new cmd.exe process, in which I then load up the Visual Studio Command Prompt to perform a build, like so:

Start-Process cmd.exe -ArgumentList $cmdArgumentsToRunMsBuildInVsCommandPrompt -WindowStyle $windowStyle -Wait

This works, and it opens a new command prompt window and I can see the build happen, and then when the build is finished the command prompt window closes. I would like to be able to get the text that is written to the command prompt window and store it in a variable, in order to inspect if the build passed or not. I tried using this, but it doesn't work; the $buildOutput variable is empty:

Start-Process cmd.exe -ArgumentList $cmdArgumentsToRunMsBuildInVsCommandPrompt -WindowStyle $windowStyle -Wait -OutVariable buildOutput
Write-Host "Build output = $buildOutput"

This makes sense since the cmd.exe process isn't returning any text; it is just writing it to it's own window. Is there a way for me to be able to store that text in a variable for the original powershell script to use? I know that I can provide a parameter to MsBuild to have it write the build log to a file, but I'm looking for a solution that doesn't involve writing to a log file and having to delete it later.

Any suggestions are appreciated. Thanks!

<# EDIT #>

Thanks for all of the responses so far! A popular suggestion has been to just call MsBuild.exe directly without using cmd.exe. The reason I need to go through cmd.exe is some projects don't build successfully if called directly from MsBuild.exe (e.g. XNA projects). Instead I need to call MsBuild.exe from the Visual Studio Command Prompt so that (I assume) all of the required environmental variables are set. I guess I could just call the VS Command Prompt directly, but it will have the same problem as calling cmd.exe too. If I can't find the VS Command Prompt I fallback to calling MsBuild.exe directly, so those answers are still appreciated.

deadlydog
  • 22,611
  • 14
  • 112
  • 118
  • 1
    Why not have it write to a log? You could use PowerShell to immediately consume the log file and delete it for you. – Matt Feb 23 '13 at 00:16
  • 2
    Just call msbuild (or appropriate executable) directly and avoid `cmd.exe`? I am not sure if `cmd.exe` "has" a stdout. Perhaps this is affected by the `/c` flag? –  Feb 23 '13 at 00:22
  • You could run it as a background job (using cmd /c) and control the display and final disposition of the output with receive-job. – mjolinor Feb 23 '13 at 01:59
  • I think pst is right here. We use msbuild directly from powershell. Works a treat. Keep it simple... – James Woolfenden Feb 23 '13 at 19:21
  • Possible duplicate of [How do I capture the output into a variable from an external process in Powershell?](http://stackoverflow.com/questions/8097354/how-do-i-capture-the-output-into-a-variable-from-an-external-process-in-powershe) – Michael Freidgeim Mar 02 '17 at 01:27

4 Answers4

17

You can always capture the output of console programs this way:

$output = [string](nuget.exe)

Here I used nuget ($output will contain the available commands list), but you can of course use msbuild.exe with the appropriate arguments.

David Brabant
  • 41,623
  • 16
  • 83
  • 111
  • 1
    This is a great solution if I want to run the build on the current process, but I was looking to run it on a new process so that it pops up in it's own window and users can use PassThru if they want so that they don't have to wait for the build to complete before their script continues execution. Upvote though for the helpful answer :) – deadlydog Apr 10 '13 at 17:23
  • This was exactly what I was looking for. Based on the title of the question alone, this answers the question 100%. Understandably, the question contents changes the requirements. Thank you for posting this answer. – Andrew T Finnell Nov 22 '16 at 20:30
3

I've solved my problem using a suggestion from the first comment on my question, which was to write the build output to a log file, consume it, then delete it. This allows me to still show the user the cmd window with the build progress if they need, as well inspect the build output once the build completes. It also still allows me to run the build in another process, so we can use PassThru if we don't want our script to wait for the build to complete before continuing execution.

I've created an Invoke-MsBuild powershell module to make building with MsBuild a snap while providing lots of parameters for additional functionality (returns if build succeeded or failed, can show/hide build window, can wait/not wait for build to finish, can automatically show build log on failed builds, etc.). You can view and download the script from my blog.

deadlydog
  • 22,611
  • 14
  • 112
  • 118
2
$process = New-Object System.Diagnostics.Process;
$process.StartInfo.UseShellExecute = $false;
$process.StartInfo.RedirectStandardOutput = $true;
$process.StartInfo.FileName = "cmd.exe";
$process.StartInfo.Arguments = $cmdArgumentsToRunMsBuildInVsCommandPrompt;
$process.Start();
$outputStream = $process.StandardOutput;
$outputStream.ReadToEnd();

You could also redirect StandardError.

muhmud
  • 4,474
  • 2
  • 15
  • 22
  • I'm sure this would work (I didn't test it yet), but it would also prevent the text from being written to the cmd window. Ideally I still want it written to the cmd window so that users can see the progress of the build, then when the build finishes I want to inspect that output to see if the build failed or not. Upvote though for the helpful answer :) – deadlydog Apr 10 '13 at 17:21
0

edit: I ended up using @David Brabant's answer

I ran into this problem and created an echo function

function echo()
{
    $input
}

which let me do this

$output = &"cmd.exe" $args | echo
ILMTitan
  • 10,751
  • 3
  • 30
  • 46