Start-Process Variables In -ArgumentList - windows

I was wondering if someone with more expertise could help me with a little problem I'm having with using Variables in -ArgumentList when using Start-Process.
If I run the Exe without using Start-Process
.\DeploymentServer.UI.CommandLine.exe register --estNumber $Number --postcode $PostCode --password $Password
everything works fine, the command runs and the software is registered.
If I try
Start-Process .\DeploymentServer.UI.CommandLine.exe -ArgumentList "register --estNumber $Number --postcode $PostCode --password $Password" -Wait -NoNewWindow
or
$Arguments = "register --estNumber $Number --postcode $PostCode --password $Password"
Start-Process .\DeploymentServer.UI.CommandLine.exe -ArgumentList $Arguments -NoNewWindow -Wait
the command runs but is unable to register, stating that it can not match the details provided. So I'm assuming the issue lies either in the passing of the arguments to Start-Process, or -ArgumentList interpreting the variables in the string. Am I missing something really simple here? Possibly to do with the $ in the -ArgumentList?

You have a space in your $postcode, so you need to put the argument in quotes:
Start-Process .\DeploymentServer.UI.CommandLine.exe -ArgumentList "register --estNumber $Number --postcode `"$PostCode`" --password $Password" -Wait -NoNewWindow

I encountered several posts online saying to wrap a Start-Process -ArgumentList argument containing spaces in 2 layers of doubles quotes, escaping the inner double quotes with a back tick `, but at least in the context I needed it, that didn't work. I found a conceptually similar solution which did work, however, i.e. a set of single quotes on the outside and a "traditional" backslash escape sequence on inner double quotes. I was guided to using this approach per this PowerShell issue post:
https://github.com/PowerShell/PowerShell/issues/5576
This example works for me (running a PS Start-Process command from cmd.exe):
C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe Start-Process -FilePath 'C:\Program Files (x86)\Example\Test.exe' -ArgumentList 'arg1','\"arg 2 w spaces\"','arg3'

Related

powershell Pipe Operators

I executed the following three powershell commands. The first two commands returned no results, and the third command returned results. The main difference between the three commands is the use of the wait argument and parentheses.
PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru | Foreach-Object -Process { $_.exitcode }
PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -wait | Foreach-Object -Process { $_.exitcode }
PS C:\Users> (Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -Wait) | Foreach-Object -Process { $_.exitcode }
1619
I test another two commands, and the difference between them was the use of parentheses. Both commands returned results no matter with parentheses.
PS C:\Users> Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru | Foreach-Object -Process { $_.id }
22980
PS C:\Users> (Start-Process -FilePath 'msiexec' -ArgumentList '/I C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet' -PassThru -Wait) | Foreach-Object -Process { $_.id }
8064
Thanks for explanation for -wait parameter. I still confused with the difference caused by parentheses. Hope for more replies.
The main difference between the three commands is the use of the -Wait argument and parentheses
To build on Mathias R. Jessen's helpful comment:
It is primarily the use of Start-Process's -Wait switch that is required:
Without -Wait, Start-Process runs asynchronously.
Without -PassThru, Start-Process produces no output.
While -PassThru makes Start-Process output a System.Diagnostics.Process instance representing the newly launched process, unless -Wait is also present that instance's .ExitCode property has no value yet, because the launched process typically hasn't exited yet.
Additionally, parentheses ((...)) are required too, because Start-Process emits the System.Diagnostics.Process instance representing the newly launched process to the pipeline (as received by ForEach-Object) right away, and then waits for the process to exit. By using (...), the grouping operator, you're forcing a wait for Start-Process itself to exit, at which point the Process' instance .ExitCode property is available, thanks to -Wait.
In general, wrapping a command in (...) forces collecting its output in full, up front - which includes waiting for it to exit - before the results are passed through the pipeline (as opposed to the streaming (one-by-on output) behavior that is the default, which happens while the command is still running).
Therefore, the following works - but see the bottom section for a simpler alternative:
# Note: With (...), you could also pipe the output to ForEach-Object, as in
# your question, but given that there's by definition only *one*
# output object, that is unnecessary.
(
Start-Process -PassThru -Wait -FilePath 'msiexec' -ArgumentList '/i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet'
).ExitCode
It follows from the above that using two separate statements would work too (given that any statement runs to completion before executing the next):
$process = Start-Process -PassThru -Wait -FilePath 'msiexec' -ArgumentList '/i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet'
$process.ExitCode
msiexec.exe is unusual in that:
it is a GUI(-subsystem) executable (as opposed to a console(-subsystem) executable), which therefore - even when invoked directly - runs asynchronously.
yet it reports a meaningful process exit code that the caller may be interested in, requiring the caller to wait for its exit (termination) in order to determine this exit code.
As an aside: For invoking console applications, Start-Process is not the right tool in general, except in unusual scenarios - see this answer.
An simpler alternative to using msiexec with Start-Process -PassThru -Wait is to use direct invocation via cmd /c, which ensures both (a) synchronous invocation and (b) that PowerShell reflects msiexec's exit code in its automatic $LASTEXITCODE variable:
cmd /c 'msiexec /i C:\Users\Downloads\Everything-1.4.1.1015.x64.msi -quiet'
$LASTEXITCODE # output misexec's exit code
Note: If the msiexec command line needs to include PowerShell variable values, pass an expandable (double-quoted) string ("...") to cmd /c instead and - as with verbatim (single-quoted) string ('...') strings - use embedded double quoting around embedded arguments, as necessary.

Powershell - Start-Process ArgumentList accepts only single variable with spaces

I'm trying to start my script from Explorer. I've found the solution and it works if script doesn't have any parameters.
$file = [System.IO.Directory]::GetCurrentDirectory() + "\Trees.ps1"
Start-Process powershell -verb runas -ArgumentList "-ExecutionPolicy UnRestricted -File `"$($file)`""
However, if I add additionall parameters just like the first it ceases to work. Ie. this code throws "The string is missing the terminator: '." error.
$file = [System.IO.Directory]::GetCurrentDirectory() + "\Trees.ps1"
$Context = [System.IO.Directory]::GetCurrentDirectory()
Start-Process powershell -verb runas -ArgumentList "-ExecutionPolicy UnRestricted -Context '"$($Context)'" -File `"$($file)`""
I'm using this way of expecting variable:
[Parameter(Mandatory=$True)]
[string]$Context,
What can I do to pass more then one variable with spaces in ArgumentList?
I suspect that I should pass arguments for file content in other way than when just passing file name, but couldn't find solution.
In addition to the back ticks issue that Theo noted, -context should follow -file.
-File
Runs the specified script in the local scope ("dot-sourced"), so that the
functions and variables that the script creates are available in the
current session. Enter the script file path and any parameters.
File must be the last parameter in the command, because all characters
typed after the File parameter name are interpreted
as the script file path followed by the script parameters.
So your command line would be
Start-Process powershell -verb runas -ArgumentList "-ExecutionPolicy UnRestricted -File `"$($file)`" -Context $($Context)"
Since you are using an external process call, you will need to use inside double quotes instead of single quotes. You can escape double quotes simply by adding another double quote ("").
Start-Process powershell -verb runas -ArgumentList "-ExecutionPolicy UnRestricted -File ""$file"" -Context ""$Context"""

Run multiple instances of same executable from powershell

I am trying to open two instances of msftpsrvr.exe(CoreFtp) on my local system simultaneously using powershell. I have two .bat files to handle that but once i run it in a function, it overrides the earlier one.
I tried with Start-Process with -Wait but it doesn't proceed to the next step until manual intervention.
function StartSFTP
{
Start-Process -FilePath "C:\SFTP\XXX-in-Reg.bat" -Wait
Start-Process -FilePath "C:\SFTP\XXX-out-Reg.bat" -Wait
}
I tried with the workflow RunScripts(solution given on stack overflow) but to no luck.
function StartSFTP
{
Start-Process -FilePath "C:\SFTP\XXX-in-Reg.bat"
Start-Process -FilePath "C:\SFTP\XXX-out-Reg.bat"
}
Current output is it opens two windows of Coreftp having XXX-in-Reg.bat credentials(port no. and path)
I am pretty much learning this powerful language and hope I was clear in stating my problem.

Suppressing The Command Window Opening When Using Start-Process

I'm trying to find a way to get PowerShell not to spawn a command window when running an executable using Start-Process.
If I call the executable directly within the script (e.g. .\program.exe) then the program runs (with its arguments) and the output is returned to the PowerShell window.
If I use Start-Process the program spawns a command window where the program runs and returns it's output.
If I try and use the -NoNewWindow switch of Start-Process the script then errors out saying it can't find the exe file.
I would prefer to use Start-Process to have access to the -Wait switch, as the programs and configurations the script makes can take some time individually to finish, and I don't want later commands starting up.
This code runs the executable in a separate command window:
Start-Process DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait
This code runs the exe within the PowerShell console:
.\DeploymentServer.UI.CommandLine.exe download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime
If I add the -NoNewWindow to the Start-Process code
Start-Process DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
I get the following error:
Start-Process : This command cannot be executed due to the error: The system
cannot find the file specifie
At C:\Temp\SOLUS3Installv1.3.ps1:398 char:22
+ Start-Process <<<< DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
You should prefix the executable name with the current directory when you use the -NoNewWindow switch:
Start-Process .\DeploymentServer.UI.CommandLine.exe -ArgumentList "download --autoDownloadOn --autoDownloadStartTime $StartTime --autoDownloadEndTime $EndTime" -Wait -NoNewWindow
Background information:
The first thing Start-Process tries to do is to resolve the value of the -FilePath parameter by PowerShell rules. If it succeeds, it replaces the value value passed with the full path to the command. If not, it leaves the value untouched.
In the Windows API there are two ways to start a new process: CreateProcess and ShellExecute. ShellExecute is the default, but if you use a cmdlet parameter that requires CreateProcess (for example, -NoNewWindow), then CreateProcess will be used. The difference between them, which matters for this question, is that when looking for a command to execute, CreateProcess uses the current process' working directory, while ShellExecute uses the specified working directory (which Start-Process by default passes based on the current filesystem-provider location, unless explicitly specified via -WorkingDirectory).
PS Test:\> 1..3 |
>> ForEach-Object {
>> New-Item -Path $_ -ItemType Directory | Out-Null
>> Add-Type -TypeDefinition #"
>> static class Test {
>> static void Main(){
>> System.Console.WriteLine($_);
>> System.Console.ReadKey(true);
>> }
>> }
>> "# -OutputAssembly $_\Test.exe
>> }
PS Test:\> [IO.Directory]::SetCurrentDirectory((Convert-Path 2))
PS Test:\> Set-Location 1
PS Test:\1> Start-Process -FilePath Test -WorkingDirectory ..\3 -Wait # Use ShellExecute. Print 3 in new windows.
PS Test:\1> Start-Process -FilePath .\Test -WorkingDirectory ..\3 -Wait # Use ShellExecute. Print 1 in new windows.
PS Test:\1> Start-Process -FilePath Test -WorkingDirectory ..\3 -Wait -NoNewWindow # Use CreateProcess.
2
PS Test:\1> Start-Process -FilePath .\Test -WorkingDirectory ..\3 -Wait -NoNewWindow # Use CreateProcess.
1
PowerShell does not update the current process' working directory when you change the current location for the FileSystem provider, so the directories can differ.
When you type:
Start-Process DeploymentServer.UI.CommandLine.exe -Wait -NoNewWindow
Start-Process cannot resolve DeploymentServer.UI.CommandLine.exe by PowerShell rules, since it does not look in the current FileSystem location by default. And it uses CreateProcess, since you specify -NoNewWindow switch. So, it ends up looking for DeploymentServer.UI.CommandLine.exe in the current process' working directory, which does not contains this file and thus causes an error.

1734: The array bounds are invalid

I have a script in which I need to run one command as an administrator. When I ran this command the script errors with a 1734 error.
My script is very basic:
runas /user:Administrator "myexec.exe \"param with spaces\" otherparam -Djava.ext.dirs=%JAVA_EXT_DIRS%"
The problem comes from the variable JAVA_EXT_DIRS which is kind of huge.
This is an old question, but I've ran into the same problem on Windows 10 with the runas command now. It turns out there's a maximum length for the program parameter, which has to be below 995 characters.
For example, this command still works:
runas /user:someuser /savecreds "cmd.exe 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
The program parameter here has 994 characters, and it should open a new command prompt. But if you add one more 1 within that parameter, the execution will fail with a 1734: The array bounds are invalid. error.
And if you increase the the program parameter even further to 1026 characters, the error changes to -2147024809: The parameter is incorrect..
The regular limit for command line parameters seems to be much much larger (I've read something about 8191 characters here on SO), so this seems to be a problem with runas.exe itself.
Edit:
I even ran into a similar problem when I tried to use a PowerShell script with the -Credential flag like this:
$username = "username"
$password = "password"
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential $username, $securePassword
$argument = $args[0]
Start-Process -FilePath "C:\path\to\my.exe" -Credential $credentials -ArgumentList "-arg $argument"
So it's probably a problem with the underlying Windows mechanics and not runas.exe itself.
The value of %JAVA_EXT_DIRS% may contain spaces too. You better put it in double quotes:
runas /user:Administrator "myexec.exe \"param with spaces\" otherparam -Djava.ext.dirs=\"%JAVA_EXT_DIRS%\""

Resources