Hi, Trying to run PowerShell code via ISE. I wanted the cmdlet Start-Process pass the command to the CMD console. Everything works fine when the destination path and the new file name do not contain a blank space in the name, such as the code below.
Start-Process -FilePath cmd.exe -ArgumentList "/c", """C:\Program Files (x86)\Internet Download Manager\IDMan.exe""", "/p", "C:\Users\Andrzej\Desktop\qap", "/h", "/n", "/q", "/f", "foo.exe", "/d", "https://www.foobar2000.org/files/foobar2000-x64_v2.24.3.exe"
The problem is when the target path to save the file or the new defined name contains an empty space. I have tried adding additional quotation marks but this does not help
File name: foo bar.exe
Target path: C:\Users\Andrzej\Desktop\qap 22
Start-Process -FilePath cmd.exe -ArgumentList "/c", """C:\Program Files (x86)\Internet Download Manager\IDMan.exe""", "/p", """C:\Users\Andrzej\Desktop\qap 22""", "/h", "/n", "/q", "/f", """foo bar.exe""", "/d", "https://www.foobar2000.org/files/foobar2000-x64_v2.24.3.exe"
The problem is certainly the location of the quotation marks, unfortunately I cannot manage with this :(
I tried changing the quotes to something else and changing Start-Process to Echo to see what it displays, In the preview it looks good but does not work.
echo -FilePath cmd.exe -ArgumentList "/c", "`"C:\Program Files (x86)\Internet Download Manager\IDMan.exe`"", "/p", "`"C:\Users\Andrzej\Desktop\qap 22`"", "/h", "/n", "/q", "/f", "`"foo bar.exe`"", "/d", "https://www.foobar2000.org/files/foobar2000-x64_v2.24.3.exe"
-FilePath
cmd.exe
-ArgumentList
/c
"C:\Program Files (x86)\Internet Download Manager\IDMan.exe"
/p
"C:\Users\Andrzej\Desktop\qap 22"
/h
/n
/q
/f
"foo bar.exe"
/d
https://www.foobar2000.org/files/foobar2000-x64_v2.24.3.exe
You've run into a cmd.exe
parsing bug triggered by command lines passed to cmd.exe /c
having both a double-quoted executable path and double-quoted arguments; you can avoid it by additionally enclosing everything that follows /c
in "..."
overall (incredibly, this does then not require escaping the then embedded "
chars), as shown in this answer.
To spell it out in the context of your call, using the syntax improvements discussed below:
# Note the additional enclosure in "..." of the entire /c argument.
Start-Process cmd.exe @"
/c ""C:\Program Files (x86)\Internet Download Manager\IDMan.exe" /p "C:\Users\Andrzej\Desktop\qap 22" /h /n /q /f "foo bar.exe" /d "https://www.foobar2000.org/files/foobar2000-x64_v2.24.3.exe""
"@
However, there is no need to use cmd /c
in your case, given that all you're doing is making a single call to an external executable, and invoking the latter directly solves your problem.
Generally, because of the unfortunate need to use embedded quoting in the individual elements of the array passed to Start-Process
's -ArgumentList
(-args
) parameter (as shown in your question), it is simpler to encode all pass-through arguments in a single string; see this answer for background information.
Therefore (using a here-string to simplify embedded quoting; implied target parameter names -FilePath
and -ArgumentList
omitted):
Start-Process "C:\Program Files (x86)\Internet Download Manager\IDMan.exe" @"
/p "C:\Users\Andrzej\Desktop\qap 22" /h /n /q /f "foo bar.exe" /d "https://www.foobar2000.org/files/foobar2000-x64_v2.24.3.exe"
"@
Note that the double-quoted i.e. expandable (interpolating) form of a here-string is used above; however, if all of your arguments are passed literally, as in this case, rather than referencing variables, you can use a '...'
, i.e. verbatim string instead (whether or not in its regular ('...'
) or its here-string form (@'<newline>...<newline>'@
).
To demonstrate a solution based on variables (this is the all-arguments-in-a-single-string equivalent of the cmd.exe /c
-free solution you later posted yourself):
$idmPath = 'C:\Program Files (x86)\Internet Download Manager\IDMan.exe'
$downloadPath = 'C:\Users\Andrzej\Desktop\qap 22'
$fileName = 'foo bar.exe'
$downloadURL = 'https://www.foobar2000.org/files/foobar2000-x64_v2.24.3.exe'
Start-Process $idmPath @"
/p "$downloadPath" /h /n /q /f "$fileName" /d "$downladUrl"
"@
Finally, given that you seem to want to invoke IDMan.exe
synchronously, you can more simply use direct invocation as follows (rather than add -Wait
to the Start-Process
call), using &
, the call operator:
& $idmPath /p $downloadPath /h /n /q /f $fileName /d $downladUrl | Write-Output
The | Write-Output
part is a trick to ensure that even GUI applications - and IDMan.exe
appears to be one - which by default execute asynchronously, then execute synchronously. Notably, direct invocation makes the need for embedded quoting go away, as shown above (PowerShell automatically double-quotes variable values containing spaces behind the scenes).
Start-Process
is usually the wrong tool. See this answer for background information and this guidance for when use of Start-Process
is and isn't appropriate.In addition to the syntactic simplification that direct invocation allows, it also has the following advantages over Start-Process
:
The process exit code of the application is reflected in the automatic $LASTEXITCODE
variable.
Start-Process
you'd have to use -PassThru
in order to obtain a process-information object and check its .ExitCode
property.If a given GUI application attaches to the caller's console, if present (which is not common), and outputs information to it, you can act on it directly (e.g., capture it in a variable); if you know a given application not to do that or you want to discard any output, you may use ... | Out-Null
instead (possibly as ... *>&1 | Out-Null
to also discard stderr output).
Start-Process
is in files, via the -RedirectStandardOutput
and -RedirectStandardError
parameters.