Start cmd from powershell with multiple arguments - powershell

Currently, I am able to call cmd from powershell using the following command:
Start-Process cmd -ArgumentList '/K "ping 192.168.1.1"'
What I am trying to do is add multiple arguments to pass onto the command, but it is not working. For example:
Start-Process cmd -ArgumentList '/K "title test" /K "ping 192.168.1.1"'
Is there a way to do this?
Edit: My goal is to have a cmd window open, pinging the address listed, but also to pass the "title" argument so the window is titled.

Since you're calling cmd.exe, use its statement-sequencing operator, &, to pass multiple commands to cmd /K:
Start-Process cmd -ArgumentList '/K title test & ping 192.168.1.1'
Note:
Start-Process's -ArgumentList (-Args) parameter technically accepts an array of arguments. While passing pass-through arguments individually may be conceptually preferable, a long-standing bug unfortunately makes it better to encode all arguments in a single string - see this answer.
cmd.exe's built-in title command inexplicably includes double quotes in the title if you enclose the argument in "..."; thus, if you want to specify a title that contains spaces, leave it unquoted and escape metacharacters other than spaces with ^; e.g., to pass test & more as the title, use title test ^& more

start-process is sometimes tricky... try to add the elements as strings to an array and pass it over to start-process.
But in your case... idk what that /K should do, but in case of ping - ping is the process to start, not cmd ;-)
start-process ping.exe -argumentlist "127.0.0.1"
start-process ping.exe -argumentlist "127.0.0.1 /t"
as you are already using PowerShell
test-connection 127.0.0.1
Here is an example where I did something simliar:
$cmdArray = #(
If ($token){
"-c"
"`"http.extraHeader=Authorization: Bearer $token`""
}
If ($clone){
'clone'
}
If ($fetch){
'fetch'
'-f'
'origin'
If ($tag){
"tags/$($tag):tags/$($tag)"
}
}
Else {
"`"$uri`""
}
If ($whatif){
'--dry-run'
}
)
$result = Start-Process $pathGitexe -ArgumentList $cmdArray -Wait -NoNewWindow -PassThru
ok, based on your comment you need this:
$ips = #("192.168.1.1","192.168.1.2")
$ips | %{
start-process ping.exe -ArgumentList $_
}

Related

Pass Arguments to Start-Process with spaces

I would like to pass the powershell arguments to an process. The arguments may contain spaces.
Powershell Code:
$proc = Start-Process -FilePath "wsl.exe" -ArgumentList $args -NoNewWindow -PassThru
$proc | Wait-Process
Run Command
powershell -noprofile -executionpolicy bypass -file sh.ps1 bash -c "`"echo Hello World`""
No Output.
Running this command with static array works fine:
$proc = Start-Process -FilePath "wsl.exe" -ArgumentList #("bash", "-c", "`"echo Hello World`"") -NoNewWindow -PassThru
$proc | Wait-Process
Output
Hello World
What I needed todo to escape the arguments from the CLI args?
See https://github.com/PowerShell/PowerShell/issues/5576 why I have to escape spaces
The obvious answer is this, with $myargs being an array of strings. "echo Hello World" needs embedded double-quotes (or single-quotes) around it, which complicates things.
$myargs = "bash", "-c", "'echo Hello World'"
$proc = Start-Process -FilePath wsl.exe -ArgumentList $myargs -NoNewWindow -PassThru
$proc | Wait-Process
Hello World
I would just do:
.\sh.ps1 bash -c 'echo hello world'
# sh.ps1 contains:
# wsl $args
Or the wsl bash for windows:
bash -c 'echo hello world'
I happened to have pwsh installed within wsl. Without the single-quotes, each word would be put on a new line.
wsl pwsh -c "echo 'hello world'"
This works for me within another powershell, with yet another set of embedded quotes:
powershell -noprofile -executionpolicy bypass -file sh.ps1 bash -c "`"'echo Hello World'`""
There's no good reason to use Start-Process in your case (see this GitHub docs issue for guidance on when Start-Process is and isn't appropriate).
Instead, use direct execution, which is not only synchronous and runs in the same console window (for console applications such as wsl.exe), but also allows you to directly capture or redirect output.
In doing so, you'll also be bypassing the longstanding Start-Process bug you mention (GitHub issue #5576), because PowerShell double-quotes arguments passed to external programs as needed behind the scenes (namely if they contain spaces).
$exe, $exeArgs = $args # split argument array into exe and pass-thru args
wsl.exe -e $exe $exeArgs # execute synchronously, via wsl -e
Note that you then also don't need to embed escaped " in your PowerShell CLI call:
powershell -noprofile -executionpolicy bypass -file sh.ps1 bash -c "echo Hello World"
However, if your arguments truly needed embedded ", you'd run into another longstanding PowerShell bug that affects only direct invocation of external programs, necessitating manual escaping with \ - see this answer.
If you do need to use Start-Process - e.g. if you want the process to run in a new (console) window - you'll have to provide embedded quoting around those $args elements that need it, along the following lines:
$quotedArgs = foreach ($arg in $args) {
if ($arg -notmatch '[ "]') { $arg }
else { # must double-quote
'"{0}"' -f ($arg -replace '"', '\"' -replace '\\$', '\\')
}
}
$proc = Start-Process -FilePath "wsl.exe" -ArgumentList $quotedArgs -PassThru
$proc | Wait-Process

Start-Process inside start-process as different user

I'm trying to pass arguments from an installshield setup file, what am I doing wrong ?
$STExecute = "C:\Files\setup.exe"
$STArgument = '/s /f2"c:\windows\setuplogs\inst.log"'
Start-Process Powershell.exe -Credential "Domain\userTempAdmin" `
-ArgumentList "-noprofile -command &{Start-Process $($STExecute) $($STArgument) -verb runas}"
I get the blow error, as you can see it removes the double quotes, that needs to be there, I cant even get it to pass the /s argument in the 2nd start-process:
Start-Process : A positional parameter cannot be found that accepts argument '/f2c:\windows\setuplogs\dmap110_inst.log'
This is happening because the inner instance is seeing /s and /f2"c:\windows\setuplogs\inst.log" as two separate positional parameters. You need to wrap the arguments for the inner Start-Process with quotes. I'd also suggest using splatting to make it easier to understand what is happening:
$STExecute = "C:\Files\setup.exe"
$STArgument = '/s /f2"c:\windows\setuplogs\inst.log"'
$SPArgs = #{
FilePath = 'powershell.exe'
ArgumentList = "-noprofile -command Start-Process '{0}' '{1}' -Verb runas" -f
$STExecute, $STArgument
}
Start-Process #SPArgs
I have also used the format operator here, as it allows us to inject values without using subexpressions. As long as there are no single quotes in $STArgument, or you escape them properly (four quotes '''' per quote in this case), it should work for you.

passing double quotes through PowerShell and msiexec

I am trying to install the Tenable Nessus agent via PowerShell script and am running into no end of issues because the Nessus MSI syntax requires a mix of no quotes and double quotes. The problem I am running into is that PowerShell needs me to escape the double quotes which seems to break the syntax understood by msiexec.exe. Let me show you what I am doing and what I have tried. I am not a strong PowerShell person, and the solution is escapes me.
Here is the code block where I set the variable with all the MSI arguments. The Nessus syntax requires that all its configs be surrounded by double quotes except for NESSUS_KEY. Earlier in the script I define the other variables.
$MSI_Arguments = #(
"/i"
"$Install_File"
"NESSUS_KEY=$Nessus_Key"
"NESSUS_SERVER=""cloud.tenable.com:443"""
"NESSUS_NAME=""$FQDN"""
"NESSUS_GROUPS=""$Nessus_Group"""
# The colon in front of a variable needs special treatment ({}).
"NESSUS_PROXY_SERVER=""${Proxy_Server}:${Proxy_Port}"""
"NESSUS_OFFLINE_INSTALL=""yes"""
"/qn"
"/norestart"
"/L*v ""$LogPath\Tenable_MSI.log"""
)
I then try running the command like so. This command does not work properly - the install proceeds, but because the parameters in -ArgumentList needs to be surrounded by double quotes, it does not get all the parameters, resulting in a broken installation.
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList $MSI_Arguments
When I put $MSI_Arguments in double quotes, I then break how PowerShell deals with the double quotes in the variable code block. I have tried the following, but none work.
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "$MSI_Arguments"
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "$$(MSI_Arguments)"
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "${MSI_Arguments}"
I have even tried just running the command straight-up, no variables, just to try and figure out the syntax. I tried two double quotes (similar to what I am trying in the variable code block) and it it works.
Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "/i C:\temp\NessusAgent-7.7.0-x64.msi NESSUS_KEY= NESSUS_SERVER=""cloud.tenable.com:443"" NESSUS_NAME=""foo"" NESSUS_GROUPS=""bar"" NESSUS_PROXY_SERVER=""proxy.company.com:80"" NESSUS_OFFLINE_INSTALL=""yes"" /qn /norestart /L*v ""C:\temp\Tenable_MSI.log"""
Now that I have a working command, I cannot figure out how to modify the variable block to work. If I just add more doubled double quotes, I get errors. If I add "" I get errors. If I add " I get errors... I am so close, yet so far.
Please, rescue me from this hell I am in. At this point I am wondering if Passing double quotes through PowerShell + WinRM has the answer, and I should base64 encode the install string, but that may be beyond my skillset given how I use variables...

Problem using a for loop and start-process to call powershell script

Could someone please help me on this. I have created two powershell script one is suppose to call the other one lets call it script2.ps1
script2.ps1 - accepts two arguments computername and the type of remediation
example:
list=get-content c:\computer.txt
foreach ($pc in $list){
start-process powershell.exe -ArgumentList "-noexit", "-file `C:\temp\client fix\script2.ps1`", "-type install", "-computer $i"
}
The intention is for each computer in the list to execute script2.ps1 on separate process. The script runs fine if start-process is not being used example:
powershell.exe -file 'C:\temp\client fix\Script2.ps1' -type install -computer $i
The Start-Process help explains:
Specifies parameters or parameter values to use when this cmdlet starts the process. If parameters or parameter values contain a space, they need surrounded with escaped double quotes.
Your file paramater does not have an escaped double quote, but a backtick that is doing nothing.
start-process powershell.exe -ArgumentList "-noexit", "-file `"C:\temp\client fix\script2.ps1`"", "-type install", "-computer $i"
Furthermore you mix arguments for powershell.exe (-noexit, -file) with arugments for your script (-type, -computer). Also your variable $i is never assigned.
Anyways, more important is to know, that there is no reason for start-process. Simplify your script by using the call operator &.
$list=get-content c:\computer.txt
foreach ($pc in $list){
& "C:\temp\client fix\script2.ps1" -type install -computer $pc
}

Start-Process and the Stop Parsing (--%) parameter

I am having trouble getting the --% parameter to work as expected. My $TaskParams variable has the character '<' which is interpreted as a redirection by powershell, therefore needs to be escaped.
However the following does not work:
$CreateTask = Start-Process PowerShell.exe "$ScriptLocation --% $TaskParams" -Wait -PassThru
Without the --%, and when I manually remove any '<' characters, it works:
$CreateTask = Start-Process PowerShell.exe "$ScriptLocation $TaskParams" -Wait -PassThru
error received:
Start-Process : A positional parameter cannot be found that accepts argument
'--%'.
note: I am using PS 5.0
Am I using the --% parameter wrong? Any help or pointers is appreciated. Thanks
The stop-parsing symbol --% only works when calling executables directly or with the call operator &; it's not for use when calling PowerShell scripts / functions / cmdlets.
You do not need to spin up a new copy of powershell.exe or use Start-Process to run a script from within another script. Just put the script command and its parameters as a line from within the other script. For example, suppose you have script2.ps1:
param(
[String] $Name
)
Write-Host "Hello, $Name"
Now suppose you also have script1.ps1:
Write-Host "This is script1.ps1"
.\Script2.ps1 -Name "Bill Stewart"
Write-Host "script1.ps1 is finished"
If you now run script1.ps1:
PS C:\> .\Script1.ps1
This is script1.ps1
Hello, Bill Stewart
script1.ps1 is finished
If you really want to use Start-Process you could encode the argument, and run it as such. I use something similar to this when elevating past UAC:
$Code = ". '$ScriptLocation' $TaskParams"
$Encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($code))
Start-Process PowerShell.exe -ArgumentList "-EncodedCommand",$Encoded -Wait -PassThru
I'm fairly certain that would accomplish what you're looking for.