In powershell spawn notepad++ when file to open has spaces in it

tl;dr

While Joel Coehoorn’s helpful answer provides an effective solution to your Start-Process problem (which stems from the bug detailed below), you can simplify your code to:

foreach ($file in $myfiles) {
  # Note: | Out-Null is a trick that makes calling *GUI* applications
  #       *synchronous* (makes PowerShell wait for them to exit).
  & $npp $file | Out-Null
}

You’re seeing a long-standing bug in Start-Process that causes it to blindly space-concatenate its -ArgumentList (-Args) arguments without using required embedded double-quoting for arguments with spaces when forming the single string encoding all arguments that is passed to the target executable behind the scenes.

  • See GitHub issue #5576, which also discusses that a fix will require a new parameter so as not to break backward compatibility.

For that reason, the required embedded double-quoting must be performed manually as shown in Joel’s answer.

When passing multiple arguments, it is ultimately easier to pass a single string to
-ArgumentList, with embedded double-quoting
as necessary – essentially by formulating a string similar to how you would pass multiple arguments from cmd.exe:

E.g., if you were to pass two file paths with spaces to Notepad++ at once, you would do:

Start-Process -Wait -FilePath $npp -ArgumentList "`"C:\bad boys\file1.txt`" `"C:\bad boys\file2.txt`""

Alternatively, since your argument string doesn’t require string interpolation, you could use a verbatim (single-quoted) string instead, which avoids the need for escaping the embedded " as `":

Start-Process -Wait -FilePath $npp -ArgumentList '"C:\bad boys\file1.txt" "C:\bad boys\file2.txt"'

Using a here-string is yet another option that avoids the need to escape, and can additionally make the call more readable (also works with single quotes (@'<newline>...<newline>'@):

Start-Process -Wait -FilePath $npp -ArgumentList @"
  "C:\bad boys\file1.txt" "C:\bad boys\file2.txt"
"@

Also note the overall simplification of the Start-Process call:

  • Use of -Wait to ensure synchronous execution (waiting for Notepad++ to exit before continuing).

    • It looks like this is what you tried to do by combining -PassThru with piping to Out-Null, but that doesn’t actually work, because that only waits for Start-Process itself to exit (which itself – unlike the launched process – executes synchronously anyway).
  • The omission of the unnecessary -NoNewWindow parameter, which only applies to starting console applications (in order to prevent opening a new console window); Notepad++ is a GUI application.

Note that the only good reason to use Start-Process here – rather than direct invocation – is the need for synchronous execution: Start-Process -Wait makes launching GUI applications synchronous (too), whereas with direct invocation only console applications execute synchronously.

If you didn’t need to wait for Notepad++ to exit, direct invocation would make your quoting headaches would go away, as the required embedded quoting is then automatically performed behind the scenes:[1]

foreach ($file in $myfiles) {
  & $npp $file # OK, even with values with spaces
}

However, the | Out-Null trick can be used effectively in direct invocation to make calling GUI applications synchronous[2], which leads us to the solution at the top:

foreach ($file in $myfiles) {
  & $npp $file | Out-Null  # Wait for Notepad++ to exit.
}

[1] However, up to at least PowerShell 7.2.x, other quoting headaches can still arise, namely with empty-string arguments and arguments whose values contain " chars. – see this answer.

[2] Out-Null automatically makes PowerShell wait for the process in the previous pipeline segment to exit, so as to ensure that all input can be processed – and it does so irrespective of whether the process is a console-subsystem or GUI-subsystem application. Since GUI applications are normally detached from the calling console and therefore produce no output there, Out-Null has no ill effects. In the rare event that a GUI application does explicitly attach to the calling console and produce output there, you can use | Write-Output instead (which also works if there’s no output, but is perhaps more confusing).

Leave a Comment