18

I'm trying to set a public property in an InstallShield installer with a value containing space. While running the MSI installer, I'm using below command on PowerShell prompt. Since the value contains a space so I used double quotes to pass the value

msiexec -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"

It breaks the command as the argument value C:\new folder\data.txt has a space in the string new folder. It results in showing up below error prompt of msiexec:

enter image description here

It suggests that arguments passed to the msiexec command has some problem.

But if I execute the same command on Windows default command prompt then it runs fine:

enter image description here

Few other options that I've tried to make things work on PowerShell prompt are as below:

  1. Using single quote in place of double quotes
  2. Using a back tick (`) character before space in the argument as per this answer.

3 Answers 3

14

Try with this

msiexec -i "myinstaller.msi" MYDIRPATH=`"C:\new folder\data.txt`"

The escape character in PowerShell is the grave-accent(`).

Sign up to request clarification or add additional context in comments.

2 Comments

Horrible! Not in my dreams I would have been able to imagine that. Thanks a ton for helping. I got some more details here for reference.
but what if i use cmd? Our customer does not have a powershell on production
9

Note:

  • This answer addresses direct, but asynchronous invocation of msiexec from PowerShell, as in the question. If you want synchronous invocation, use Start-Process with the -Wait switch, as shown in Kyle 74's helpful answer, which also avoids the quoting problems by passing the arguments as a single string with embedded quoting.

  • Additionally, if you add the -PassThru switch, you can obtain a process-information object that allows you to query msiexec's exit code later:

# Invoke msiexec and wait for its completion, then
# return a process-info object that contains msiexec's exit code.
$process = Start-Process -Wait -PassThru msiexec '-i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"'
$process.ExitCode
  • Note: There's a simple trick that can make even direct invocation of msiexec synchronous: pipe the call to a cmdlet, such as Wait-Process
    (msiexec ... | Wait-Process) - see this answer for more information.

To complement Marko Tica's helpful answer:

Calling external programs in PowerShell is notoriously difficult, because PowerShell, after having done its own parsing first, of necessity rebuilds the command line that is actually invoked behind the scenes in terms of quoting, and it's far from obvious what rules are applied.

  • Note:
    • While the re-quoting PowerShell performs behind the scenes in this particular case is defensible (see bottom section), it isn't what msiexec.exe requires.
    • Up to at least PowerShell 7.1, some of the re-quoting is downright broken, and the problems, along with a potential upcoming (partial) fix, are summarized in this answer.
    • Marko Tica's workaround relies on this broken behavior, and with the for now experimental feature that attempts to fix the broken behavior (PSNativeCommandArgumentPassing, available since Core 7.2.0-preview.5), the workaround would break. Sadly, it looks like then simply omitting the workaround won't work either, because it was decided not to include accommodations for the special quoting requirements of high-profile CLIs such as msiexec - see GitHub issue #15143.

To help with this problem, PSv3+ offers --%, the stop-parsing symbol, which is the perfect fit here, given that the command line contains no references to PowerShell variables or expressions: --% passes the rest of the command line as-is to the external utility, save for potential expansion of %...%-style environment variables:

# Everything after --% is passed as-is.
msiexec --% -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"

If you do need to include the values of PowerShell variables or expressions in your msiexec call, the safest option is to call via cmd /c with a single argument containing the entire command line; for quoting convenience, the following example uses an expandable here-string (see the bottom section of this answer for an overview of PowerShell's string literals).

$myPath = 'C:\new folder\data.txt'

# Let cmd.exe invoke msiexec, with the quoting as specified.
cmd /c @"
msiexec --% -i "myinstaller.msi" MYDIRPATH="$myPath"
"@

If you don't mind installing a third-party module, the ie function from the Native module (Install-Module Native) obviates the need for any workarounds: it fixes problems with arguments that have embedded " chars. as well as empty-string arguments and contains important accommodations for high-profile CLIs such as msiexec on Windows, and will continue to work as expected even with the PSNativeCommandArgumentPassing feature in effect:

# `ie` takes care of all necessary behind-the-scenes re-quoting.
ie msiexec -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"

As for what you tried:

PowerShell translated
MYDIRPATH="C:\new folder\data.txt" into
"MYDIRPATH=C:\new folder\data.txt" behind the scenes - note how the entire token is now enclosed in "...".

Arguably, these two forms should be considered equivalent by msiexec, but all bets are off in the anarchic world of Windows command-line argument parsing.

8 Comments

Cool! msiexec --% -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt" also works. In fact this is a more cleaner solution than trying that magical back tick character as escape sequence.
@RBT: Indeed: because in your case you don't need PowerShell to interpret the command line at all, --% is simplest.
True. In fact, in reality I've got several command line arguments for msiexec which can have spaces in them.I showed only one command line argument just to ensure simplicity in my post. When there are several command-line arguments with a possibility of space in them, applying back tick escape sequence would become a nightmare.
If you are stuck with PowerShell 2.0 on older systems, you can use a command-line tool such as showargs.exe (see Windows IT Pro - Running Executables in PowerShell) to get a look at the actual command line that PowerShell passes to an executable.
This answer solved my problem too. I was executing msiexec from Powershell with a path as a msi property. --% solved my problem. Thanks.
|
1

This is the best way to install a program in general with Powershell.

Here's an example from my own script:

start-process "c:\temp\SQLClient\sqlncli (x64).msi" -argumentlist "/qn IACCEPTSQLNCLILICENSETERMS=YES" -wait

Use Start-Process "Path\to\file\file.msi or .exe" -argumentlist (Parameters) "-qn or whatever" -wait.

Now -wait is important, if you have a script with a slew of programs being installed, the wait command, like piping to Out-Null, will force Powershell to wait until the program finishes installing before continuing forward.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.