Search code examples
powershell

Why do I get this error when I try to set my powershell terminal title in my ps1 script?


I have a .ps1 script that opens multiple powershell terminals and in each one it starts a nodejs application. In order to make it easier to tell which terminal corresponds to which node application, I want to set the title of the terminal. I do so with $host.ui.RawUI.WindowTitle = "my application". Here is a sample of the script:

start powershell {
    $host.ui.RawUI.WindowTitle = "application 1"
    cd application1\backend
    npm install
    npm run build
    npm run start
    Read-Host
}

start powershell {
    $host.ui.RawUI.WindowTitle = "application 2"
    cd application2\backend
    npm install
    npm run build
    npm run start
    Read-Host
}

start powershell {
    $host.ui.RawUI.WindowTitle = "application 3"
    cd application3\backend
    npm install
    npm run build
    npm run start
    Read-Host
}
...

There are more than 3 such blocks in the script, but you get the point.

The problem is I get this error in the terminal:

application : The term 'application' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:3 char:31

  • $host.UI.RawUI.WindowTitle = application 1
  •                           ~~~~~~~~~~~
    
    • CategoryInfo : ObjectNotFound: (application:String) [], CommandNotFoundException
    • FullyQualifiedErrorId : CommandNotFoundException

I know $host.UI.RawUI.WindowTitle = "application 1" works from the terminal prompt. Why doesn't it work in a script?


Solution

  • Passing a script block ({ ... }) as a (positionally implied) -ArgumentList argument to Start-Process (one of whose built-in aliases is start) technically works, but is limiting and obscures what's actually happening, so it is best avoided:

    • Because the -ArgumentList parameter is [string[]]-typed, the script block is stringified, meaning that its verbatim content (sans { and }) is passed.

    • Notably, this implies that you cannot reference the caller's variables in your argument, and it may lead you to think that there are no escaping requirements (see next point).

    Aside from that, your problem is that you neglected to escape " characters as \", which is required in order to preserve them in code passed to the (implied) -Command (c) parameter of powershell.exe, the Windows PowerShell CLI.

    • See this answer for background information.

    • Also note that pwsh.exe, the PowerShell (Core) 7 CLI, defaults to the -File parameter, necessitating explicit use of -Command (-c) in order to pass PowerShell code.


    Therefore, it is better to use a here-string, with the required escaping:

    # Note: Short for:
    #   Start-Process -FilePath powershell -ArgumentList @'...
    start powershell @'
        $host.ui.RawUI.WindowTitle = \"application 1\"
        cd application1\backend
        npm install
        npm run build
        npm run start
        Read-Host
    '@
    

    Note:

    • Because ' characters have no syntactic function on the PowerShell process command line, they do not require escaping, so you can alternatively use embedded '...' quoting as shown in Keith Langmead's answer - however, '...' only works if the embedded string literal is meant to be a verbatim string rather than an expandable (interpolating) one (which requires "...").

    • Because no up-front string interpolation is required in your case (since you're not referencing the caller's variables), the above uses the verbatim here-string variant (@'<newline>...<newline>'@); use the expandable form (@"<newline>...<newline>"@) if caller-variable values must be referenced.