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?
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.