Debugging a Powershell start-process line - powershell

I'm using Powershell's standard ISE to develop a script that, among other things, calls start-process using a pre-defined filepath. Here is the code:
$MSTEST ="C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\MSTEST.EXE"
$TESTSETTING="D:\Source\Test\DEV\FIREBIRD\QA\LoadTesting\WebTests\perfvsctlr2.testsettings"
$TESTCONTAINER1="D:\Source\Infinity\DEV\FIREBIRD\QA\LoadTesting\WebTests\Test.AppFx.LoadTesting.Test\LoadTestDefs\Test_Interactive_Peak_Workload.loadtest"
start-process $MSTEST -ArgumentList "/Testsetting: $TESTSETTING /Testcontainer: $TESTCONTAINER1 /resultsfile: $RESULTSFILE"
When I pass the variables and then try to manually execute the start-process line from the Powershell prompt it simply opens a window and closes it without displaying the error. So far I've used the -NoNewWindow argument and tried calling Powershell from the Run line with the -noexit argument. So far, no luck.

How is $RESULTSFILE defined?
You shouldn't have a space after the colon (and you generally have to look out for spaces in any of your arguments. You don't seem to have any, but spaces in arguments require proper quoting).
You can get the error message from Start-Process by calling it with some extra arguments (the line below produces an error on purpose):
Start-Process $MSTEST -ArgumentList "\testcontainer: file" -wait -NoNewWindow -RedirectStandardError err -RedirectStandardOutput out

Related

Powershell start-process issue when path contains R character after parenthesis

I am trying to install msi from commandline using powershell command. My msi path contains R character after parenthesis. Due to this powershell throws below error.
PowerShell.exe -ExecutionPolicy Bypass Start-Process -Wait -FilePath msiexec -ArgumentList /i, `"c:\program files\xxx (R) xxx.msi`"
If my path contain no parenthesis, it is working fine. Can someone help me resolve this issue.
Do not suggest some other method to do it. I would like to know the path issue as it works when my msi path doesnt have parenthesis.
From PowerShell:
# Note that parameter -FilePath is implied for `msiexec`, and
# -ArgumentList for `'/i ...'`
Start-Process -Wait msiexec '/i "c:\program files\xxx (R) xxx.msi"'
The most robust approach is to use a single -ArgumentList (-Args) argument that encodes all arguments, using embedded quoting, rather than passing arguments individually, due to a long-standing bug detailed in this answer.
From cmd.exe, there's no reason to involve PowerShell in this case; the built-in start command will do:
start /wait msiexec /i "c:\program files\xxx (R) xxx.msi"

How can I properly escape a space in ArgumentList of Start-Process command nested within an ArgumentList of Start-Process command?

For a long time I've been using a powershell script containing the following line to automatically start various processes as a different user AND with elevated permissions:
start-process powershell -credential <username> -argumentlist '-command &{start-process <executable.exe> -verb runas -argumentlist <c:\path\to\file\that\executable-should-open.ext>}' -workingdirectory c:\windows\system32
Formatting it this way lets me simply create a convenient shortcut to the script. This works fine... as long as <c:\path\to\file\that\executable-should-open.ext> contains no spaces. However that file is now located at a filepath which contains spaces and that's something I cannot change.
There's countless threads out there dealing with how to properly escape the -ArgumentList parameter argument(s) of Start-Process, but none of the solutions seem to work for this case where the space is in the nested -ArgumentList. I've tried for days to find the magic sauce combination of single and double quotes, backticks, backslashes, $variables, $($variables), #("arguments","as","arrays"), not using &, etc., etc.
Can anyone help me achieve this? I'm certainly open to other solutions that don't use Start-Process, but ideally the working solution should work in a standalone script, and once run, should leave no additional console windows open.
With most attempts that don't outright throw errors, the behavior I'm seeing is that c:\path\to\file\with spaces\file.ext is interpreted by the nested Start-Process cmdlet as two separate arguments, i.e. c:\path\to\file\with and spaces\file.ext.
I use this syntax to open several different administrative apps, but as a concrete example, here is the one I've been testing and failing to make work.
Original, working script:
start-process powershell -credential domain\username -argumentlist '-command &{start-process mmc -verb runas -argumentlist c:\custom-aduc.msc}' -workingdirectory c:\windows\system32`
New, non-working script:
start-process powershell -credential domain\username -argumentlist '-command &{start-process mmc -verb runas -argumentlist c:\new path\custom-aduc.msc}' -workingdirectory c:\windows\system32
For reference, the reason I can't change the path is because the custom-aduc.msc is now being kept on OneDrive, which infuriatingly provides no way to change its local sync location from drive:\path\OneDrive - CompanyName. This is a heavily supported feature request for OneDrive, but has not been committed to.
Thanks for any insights.
P.S. I've considered alternate solutions such as:
Having the script first download/copy the files to a local path without spaces, however, this prevents me from using the executable to modify the file, or I'd have to monitor changes and re-upload them.
Mounting the OneDrive path as a separate drive letter. But that's much less of a standalone solution, and seems heavy handed.
TL;DR: To answer the titular question: It might be possible, but it's much easier to simply split the Start-Process commands into different scripts, to avoid nesting one inside the quotes of the other.
After lots more trial and error with escape syntax, I was able to find an acceptable solution. However I had to split it up into two separate script files, so as to avoid nesting the second Start-Process (and thus its -ArgumentList arguments) inside any other quotes.
script1.ps1
#runas /user:domain\username "powershell -file \`"c:\script2.ps1\`""
# Equivalent to the above, but provides a nicer credential prompt:
Start-Process -FilePath powershell -Credential domain\username -WorkingDirectory "c:\windows\system32" -ArgumentList "-File `"c:\script2.ps1`""
script2.ps1
Start-Process -FilePath mmc -Verb runas -ArgumentList "`"C:\path\with spaces\aduc.msc`""
The important part is that this way I can use the usual escaping methods for each -ArgumentList, without them interfering with each other. It may still be possible to use combinations of cmd interpreter escapes (\), and posh escapes (`) to do this all in one line, but I don't have the patience to figure that out. It also may be possible to hack this into a single script, but I can't be bothered.
At this point, I can simply create shortcuts which call script1.ps1 to achieve the desired result, just like I used to.
I like to use this for at least 3 different executables, and with 3 different usernames (my average joe account, and two different superuser accounts). To avoid needing two scripts for each executable+username combination, I generalized the scripts like so:
script1.ps1
$app = $args[0]
$user = $args[1]
$script2 = "C:\path\to\script2.ps1"
# Dont run as different user if this is my usual Windows login
# Otherwise I have to enter a password for no good reason
if($user -eq "domain\non-su-account") {
& powershell -file "$script2" $app
}
else {
#runas /user:$user "powershell -file \`"$script2\`" $app"
# Equivalent to the above, but provides a nicer credential prompt:
Start-Process -FilePath powershell -Credential $user -WorkingDirectory "c:\windows\system32" -ArgumentList "-File `"$script2`" $app"
}
script2.ps1
$app = $args[0]
switch($app) {
"aduc" {
Start-Process -FilePath mmc -Verb runas -ArgumentList "`"C:\path\with spaces\aduc.msc`""
break
}
"posh" {
Start-Process -FilePath powershell -Verb runas -ArgumentList "-nologo -noexit -command `"&{. \`"C:\path\with spaces\custom-powershell-profile.ps1\`"}`""
break
}
"posh-noprofile" {
Start-Process -FilePath powershell -Verb runas -ArgumentList "-nologo -noexit"
break
}
"mecm" {
& "C:\Program Files (x86)\Microsoft Endpoint Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.exe"
break
}
}
So the shortcut targets look like:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -file "C:\path\to\script1.ps1" aduc domain\username
Opening powershell and forcefully dot sourcing my custom profile script isn't really necessary, but it avoids the need to touch the powershell profiles of the superuser accounts, making this more portable.
Now that I've thoroughly demonstrated the weakness with which most markdown interpreters render nested/escaped powershell quotes, I bid you a good day. Hopefully this is useful to at least one other person ever again.

Powershell Removing Quotes Argument

I'm using Start-Process to start another instance of Powershell as an administrator but when I try to pass the argument list, whether as a variable or as a plain string, Powershell removes the quotes. Below is the command I'm using:
$argu = '-noexit "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"';
powershell Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
This is the error I get:
x86 : The term 'x86' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
At line:1 char:88
+ ... Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\v ...
+ ~~~
+ CategoryInfo : ObjectNotFound: (x86:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Thank you in advance for any help.
Update:
$argu = '''-noexit ""C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat""''';
powershell Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
This almost fixes it but now I'm getting the error above in the second window instead of the first.
(A) From inside PowerShell:
$argu = '-noexit -command & \"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat\"'
Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
Note: I'm not calling Start-Process via powershell.exe, as there is generally no need for that.
The embedded " are \-escaped, which is what PowerShell requires when you call its CLI (perhaps surprisingly, given that PowerShell-internally it is ` that acts as the escape character).
That said given that the " are embedded inside '...' here, they shouldn't require extra escaping - see below.
The file path to execute is prefixed with call operator &, because you need it in order to execute files that are specified in quoted form.
Note that I've added -Command, which is not strictly necessary in Windows PowerShell, but would be if you ran your command from PowerShell Core (which now defaults to -File).
Alternatively, you could also specify your arguments individually, as part of an array, which is arguably the cleaner solution:
$argu = '-noexit', '-command', '&', 'de',
'\"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat\"'
Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
Sadly, even in this case you need the extra, embedded quoting for arguments that contain spaces, which is a known Start-Process problem being tracked on GitHub.
PowerShell's handling of quoting when calling external programs is generally problematic; the current issues are summarized in this GitHub issue.
(B) From outside PowerShell (cmd.exe, a custom File Explorer context menu):
powershell -command Start-Process -Verb RunAs -FilePath powershell -ArgumentList '-noexit -command . ''C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat'''
single-quoting is now employed (with nested single quotes escaped as ''), because double-quoting would substantially complicate the escaping.
. is used instead of & to execute the .bat file, which avoids a problem with how the & is parsed; while . generally serves a different purpose than &, the two operators behave the same when calling external programs.
If you also want to set the working directory for the PowerShell session that ultimately opens elevated, you need to incorporate an explicit Set-Location (cd) call into the command string, because Start-Process -Verb RunAs always defaults to the SYSTEM32 folder (even the -WorkingDirectory parameter doesn't help in that case).
For that to work safely, however, you must quote the directory path using double-quoting, given that file names may contain single quotes; with %V as the directory path (which File Explorer supplies to commands invoked via custom context menus), the properly escaped Set-Location call looks like this (I wish I were kidding):
Set-Location \"\"\"%V%\"\"\"
Integrated into the full command (using Set-Location's built-in alias cd for brevity):
powershell -command Start-Process -Verb RunAs -FilePath powershell -ArgumentList '-noexit -command cd \"\"\"%V%\"\"\"; . ''C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat'''
As an aside: PowerShell Core now has a -WorkingDirectory (-wd) CLI parameter that allows you to control the startup directory more robustly (pwsh -wd "c:\path\to\dir" ...); in fact, it was precisely the File Explorer custom context-menu use case that prompted the introduction of this parameter.

Run powershell in new window

I would like to run new powershell window with parameters. I was trying to run following:
powershell -Command "get-date"
but everything happens in same console. Is there a simple way to do this?
To open a new PowerShell Window from PowerShell:
Start-Process PowerShell
Or my personal favorite:
Start-Process PowerShell -WindowStyle Maximized
Then you could typeGet-Datewithout having to deal with the -ArgumentList's tendency to close itself. I still haven't found a way to open a new PowerShell process with the -ArgumentList Parameter, without it immediately closing after it runs. For Instance:
Start-Process PowerShell -ArgumentList "Get-Date"
or simply
Start-Process PowerShell -ArgumentList Get-Date
Will Close Immediately after running the process.
In order to get it to wait before closing you could add:
Start-Process PowerShell -ArgumentList 'Get-Date; Read-Host "Press Enter"'
Since the -Wait Parameter seems to do nothing at all in this case.
FYI - PowerShell Suggested Syntax is actually:
Start-Process -FilePath "powershell.exe"
But since PowerShell is a standard Windows Application in the %SystemRoot%\system32 Environment Variables the command line(s) should recognize a simple
Powershell
Command
Use the start command. In a CMD prompt, try:
start powershell -noexit -command "get-date"
For Start/Run (or Win+r) prompt, try:
cmd /c start powershell -noexit -command "get-date"
The -noexit will tell Powershell to, well, not to exit. If you omit this parameter, the command will be executed and you are likely to just see a glimpse of a Powershell window. For interactive use, this is a must. For scripts it is not needed.
Edit:
start is an internal command for CMD. In Powershell it is an alias for Start-Process. These are not the same thing.
As for why the window is black, that's because the shortcut for Powershell.exe is configured to set the background blue.
To call a PowerShell (PS) script in a second terminal window without exiting, you can use a script similar to:
Start-Process PowerShell -ArgumentList "-noexit", "get-date"
or if you need to run another script from a specific location:
Start-Process PowerShell -ArgumentList "-noexit", "-command .\local_path\start_server.ps1"

PowerShell - Start-Process and Cmdline Switches

I can run this fine:
$msbuild = "C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe"
start-process $msbuild -wait
But when I run this code (below) I get an error:
$msbuild = "C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe /v:q /nologo"
start-process $msbuild -wait
Is there a way I can pass parameters to MSBuild using start-process? I'm open to not using start-process, the only reason I used it was I needed to have the "command" as a variable.
When I have
C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe /v:q /nologo
on a line by itself, how does that get handled in Powershell?
Should I be using some kind of eval() kind of function instead?
you are going to want to separate your arguments into separate parameter
$msbuild = "C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe"
$arguments = "/v:q /nologo"
start-process $msbuild $arguments
Using explicit parameters, it would be:
$msbuild = 'C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe'
start-Process -FilePath $msbuild -ArgumentList '/v:q','/nologo'
EDIT: quotes.
Warning
If you run PowerShell from a cmd.exe window created by Powershell, the 2nd instance no longer waits for jobs to complete.
cmd> PowerShell
PS> Start-Process cmd.exe -Wait
Now from the new cmd window, run PowerShell again and within it start a 2nd cmd window:
cmd2> PowerShell
PS> Start-Process cmd.exe -Wait
PS>
The 2nd instance of PowerShell no longer honors the -Wait request and ALL background process/jobs return 'Completed' status even thou they are still running !
I discovered this when my C# Explorer program is used to open a cmd.exe window and PS is run from that window, it also ignores the -Wait request.
It appears that any PowerShell which is a 'win32 job' of cmd.exe fails to honor the wait request.
I ran into this with PowerShell version 3.0 on windows 7/x64
I've found using cmd works well as an alternative, especially when you need to pipe the output from the called application (espeically when it doesn't have built in logging, unlike msbuild)
cmd /C "$msbuild $args" >> $outputfile
Unless the OP is using PowerShell Community Extensions which does provide a Start-Process cmdlet along with a bunch of others. If this the case then Glennular's solution works a treat since it matches the positional parameters of pscx\start-process : -path (position 1) -arguments (positon 2).