Invoking a PowerShell script from cmd.exe (or Start | Run)

Earlier today Ying Li over at myITforum.com posted an article about some of the difficulty involved in launching a Windows PowerShell script from cmd.exe (or Start | Run) when there is a space in the path to the ps1 file containing the script.  In this article, Ying was pointing out how running a script with a command similar to the following will result in an error:

powershell -noexit & “C:\Documents and Settings\poshoholic\My Documents\PowerShellRocks.ps1”

The reason this generates an error is simply because of how the command line arguments are broken into tokens outside of Powershell and then rebuilt as arguments inside of PowerShell.  In the example above, PowerShell is being executed with 3 arguments: -noexit, &, and the path to the PowerShell script.  Even though the PowerShell script has quotation marks around it when it is entered in cmd.exe or in the Start | Run dialog, those quotation marks are only used to hold the string together as one argument outside of PowerShell.  They are not passed in with the string.  When PowerShell actually gets to see the arguments inside, it sees this:

  1. -noexit
  2. &
  3. C:\Documents and Settings\poshoholic\My Documents\PowerShellRocks.ps1

The -noexit argument is a named switch property that is supported by PowerShell, so PowerShell knows to keep the console open when the script is done.  The remaining arguments aren’t associated with a named property, so PowerShell treats them as the command by concatenating them together with a space between the tokens.  This makes the PowerShell command look like this:

& C:\Documents and Settings\poshoholic\My Documents\PowerShellRocks.ps1

If you try to run this command in PowerShell, you will get an error because the parser doesn’t know what to do with the C:\Documents token.  Fortunately, the fix to this is simple.  As Ying pointed out, you can use single quotes instead of double quotes.  This means you could enter the command like this:

powershell -noexit & ‘C:\Documents and Settings\poshoholic\My Documents\PowerShellRocks.ps1’

In this case, the single quotes are not used to identify one argument to PowerShell.exe, so the argument list looks like this:

  1. -noexit
  2. &
  3. ‘C:\Documents
  4. and
  5. Settings\poshoholic\My
  6. Documents\PowerShellRocks.ps1′

Since the single quotes weren’t removed when the command was broken into tokens, the string remains intact when it is rebuilt as the command to execute within PowerShell.

But what if you really needed to use double quotes?  Well, double quotes work too, but you have to treat them as a special case because they are used to identify an argument that contains spaces.  More specifically, you have to escape them within a surrounding pair of quotes by entering two double quotes side by side.  In the case of our example, that means entering the command like this: 

powershell -noexit “& “”C:\Documents and Settings\poshoholic\My Documents\PowerShellRocks.ps1″””

Running this command results in two arguments being passed into PowerShell:

  1. -noexit
  2. & “C:\Documents and Settings\poshoholic\My Documents\PowerShellRocks.ps1”

Bingo, that’s the script that we originally intended to run!

While it might not be that common that someone wants to pass quotation marks into PowerShell via cmd.exe or Start | Run, I thought it would be useful to clarify this for whenever the need arises.  In general, I recommend always putting external quotation marks around the entire command that you want to pass into PowerShell this way and using paired double-quotes or non-paired single quotes within that quoted command as required.  This prevents unnecessary tokenizing and then rebuilding of the command which, as Ying’s article illustrated, doesn’t always rebuild as you would expect it to.

For related information, specifically pertaining to the order you need to use in your PowerShell.exe arguments, read this post.

Kirk out.

Technorati Tags: , ,

18 thoughts on “Invoking a PowerShell script from cmd.exe (or Start | Run)

  1. I ran into the same need for double quotes when executing .VBS files with parameters from a C# Application.

    I was looking to execute a .PS1 file from a C# app and I am gald I came across this info.

    Thank you!

  2. Thanks for the info and examples. i guess it goes without saying, but it’s important to distinguish typographical open and close quotes from the simple ascii variety intended here. folks coming from unix background might be confused, because “back-quote” had a special meaning there. I’ll be curious how my ‘quotes’ appear after posting. 🙂

  3. Thanks for the explanation.

    Unfortunately, this is one of the many rough edges to powershell that make it a pain. Powershell needs to become better integrated into windows before I will really start using it. The end result that Yang Li pointed out — confusion — is all too prevalent in Powershell.

    1. Confusion comes with any new technology, and PowerShell is definitely not immune to that. There are some things that don’t seem to make sense. We call them “gotchas”.

      I’d love to hear more about “the many rough edges to PowerShell that make it a pain” to see if I can do anything to help.

      Also I’d like to know what sort of integration you are looking for with PowerShell and Windows. With Windows 7 and Windows Server 2008 R2, it is shipped as part of the operating system and installed by default, which is a great first step. But I’d like to know what specific scenarios come to mind for you when you think about stronger integration into Windows. Please elaborate if you have time. 🙂

  4. Hello,

    I’m replying to this old post because it helped me to find a way to dynamically call / run program requiring arguments like :

    C:\Program files\Program\program.exe /verysilent /norestart /LoadInf=”.\conf.ini”

    This work if I write directly the full command in powershell like :

    & C:\Program files\Program\program.exe /verysilent /norestart /LoadInf=".\conf.ini"
    

    Great!
    But I have no idea of the command in advance… Heck!
    So I tried several things like :

    & $fullCommandString
    & $commandString $argsString
    . $commandString $argsString
    . $quotedCommandString $quotedArgsString
    powershell -command ""
    invoke-item $commandString $argsString
    

    etc……..

    but I still had errors caused by space in “Program files” or slash of arguments etc…

    The way I found is to pass a string for the command and an array of string for arguments :
    (here I directly wrote the command in the code by in reality, $command is feeded from and XML file)

    $command = "C:\Program files\Program\program.exe /verysilent /norestart /LoadInf='.\conf.ini'"
    if($command -match "(?.*\.[A-Za-z]+\s)(?.*)"){
       $args = [regex]::Split($Matches.appArgs.trim(), "\s" )
       & $Matches.appPath.trim() $args
    }
    

    Hope thats helps.

    Marc Magnin,
    Pourquoi faire simple quand on peut faire compliqué ?!

  5. I’have just found a better way by using the [diagnostics.process] class cause i need to wait the end of execution :

     if($command -match "(?.*\.[A-Za-z]+\s)(?.*)"){
         $com =$Matches.appPath.trim()
         $args =  $Matches.appArgs.trim()
         [diagnostics.process]::start($com, $args).WaitForExit()
    }
    

    Marc Magnin,
    Pourquoi faire simple quand on peut faire compliqué ?!

  6. Intesting, but what about this scenario:

    From a command prompt
    C:\>PowerShell.exe “C:\Some Long Path\Script.ps1” “C:\Another Long Path To Some Input File.txt”

    The Powershell script itself will execute, but it’s choking on the .txt file. Single quotes on the .txt file don’t file the problem.

    1. For your example command to work from outside of PowerShell (for example, in cmd.exe), you need to run it like this:

      PowerShell “& “”””C:\Some Long Path\Script.ps1″””” “”””C:\Another Long Path To Some Input File.txt”””””

      Here’s how this breaks down. First the string is evaluated where it is executed (in cmd.exe). When the evaluation replaces adjacent double-quotes with a single double-quote, our command string resolves to this:

      & “”C:\Some Long Path\Script.ps1″” “”C:\Another Long Path To Some Input File.txt””

      Then this string is passed into PowerShell as a double-quoted string. In the double-quoted string, a pair of adjacent double-quotes are again converted to one double-quote, and the string command resolves to this:

      & “C:\Some Long Path\Script.ps1” “C:\Another Long Path To Some Input File.txt”

      That’s the command you want to execute, so you’re good to go.

      This gets a lot easier in PowerShell 2.0 with some new arguments on the powershell.exe command.

      If you wanted to run the same command from within PowerShell, you would do it like this:

      PowerShell “& “”C:\Some Long Path\Script.ps1″” “”C:\Another Long Path To Some Input File.txt”””

  7. But the bigger question is,

    When you run a cmd file lets say the following from with in your powershell script
    & “C:\ABC (QA)_1.0.7.2\en-us\ABC_QA.cmd”

    Contents of ABC_QA.cmd
    Msiexec /i ABCInstaller.msi ^
    DB.SERVER=ABC\QA ^
    APPLICATION.ENV.TYPE=Qa ^
    SVCIDENTITY=SVC-QA@ABC.com ^
    SVCPASSWORD=xxx ^
    LOCAL.EMAILING=”true” ^
    /lv “ABC_Installer_QA_Log.txt”

    However when the powershell script is run, it starts the cmd from C:\Documents and Settings\tarora\My Documents and fails to find the correct path to the MSI.

    If i make the msi available at ‘C:\Documents and Settings\tarora\My Documents’ it works fine.

    1. Am i missing anything?
    2. Is there a way to specify the start path for the cmd?

    1. In this case I would use Start-Process. Start-Process has a -WorkingDirectory parameter that allows you to specify the working directory for the process you start. I believe this should give you the results that you are looking for.

  8. Is there a way to run a Powershell command from the run dialog box and also pass parameters to the script you want to run?

    1. Sure. You just need something like this:

      powershell.exe -command “& C:\test.ps1 parameter1 parameter2”

      You could use named parameters of course. The trick is to make sure you are passing the parameters from inside of your PowerShell session that you are creating, not as parameters to powershell.exe.

  9. I’m actually fighting also with spaces and powershell. I’ll try to send an email from the Windows CMD Shell via powershell, but it does not work.

    I tried it like this:
    powershell.exe -command send-mailmessage -to “to@domain.com” -from “from@domain.com” -subject “My Testmail” -smtpserver “10.10.10.10” -body “This is a test with spaces.”

    It only works if I write it like this:
    powershell.exe -command send-mailmessage -to “to@domain.com” -from “from@domain.com” -subject “My_Testmail” -smtpserver “10.10.10.10” -body “This_is_a_test_with_spaces.”

    Any ideas why this also is broken into serveral tokens ?

    1. Sorry for the delay, I just noticed this message in my spam filter.

      The quotes are removed as you pass from cmd.exe into PowerShell. As a result, you need to pass in quotes inside of your quotes, either single quotes (in which case you only need one) or double-quotes (in which case you need two in order to pass in a single double-quote from within a double-quoted string). Like this:

      powershell.exe -command send-mailmessage -to “to@domain.com” -from “from@domain.com” -subject “””My Testmail””” -smtpserver “10.10.10.10” -body “””This is a test with spaces.”””

      Or you could get rid of a bunch of those quotes that are not necessary and just do it like this (which I think is cleaner):

      powershell.exe -command send-mailmessage -to to@domain.com -from from@domain.com -subject “‘My Testmail'” -smtpserver 10.10.10.10 -body “‘This is a test with spaces.'”

      Kirk out.

Leave a comment