How to properly escape quotes in powershell v2?

How do you properly escape quotes in powershell v2 (called from within a batch file)?

I have tried:

powershell -Command "(gc file1.txt) -join "`n" | Out-File file2.txt"

and

powershell -Command "(gc file1.txt) -join ""`n"" | Out-File file2.txt"

and

powershell -Command "(gc file1.txt) -join '"`n`" | Out-File file2.txt"

but they all fail.

Editor's note: The purpose of the command is to transform Windows CRLF line breaks to Unix LF-only ones, so as to create a file that will be processed on Linux.


Solution 1:

From a batch file (cmd.exe), you must \-escape embedded " instances (even though PowerShell-internally it is ` (the backtick) that serves as the escape character):

As wOxxOm points out in a comment on the question, in Windows PowerShell using """ to embed a single " is also an option. However, given that most command-line utilities support \", \" is easier to remember. However, both """ and \" can break on the cmd.exe side, in which case "^"" (sic) is required ("" in PowerShell (Core) 7+).

powershell -Command "(gc file1.txt) -join \"`n\" | Set-Content -NoNewLine file2.txt"

Note:

  • Set-Content -NoNewline requires PSv5+.

  • Set-Content writes "ANSI"-encoded files by default (e.g., based on code page Windows-1252 on US-English systems); use the -Encoding parameter to change that.

    • Since you're only dealing with strings, Set-Content is preferable to Out-File, which is only needed if you have non-string objects that must have PowerShell's default formatting applied to them first.
  • Consider using powershell -NoProfile ... to suppress loading of PowerShell's profile files, both for faster execution and for a more predictable execution environement.


PSv2 solution:

Unfortunately, prior to PSv5, only the Write-Host cmdlet supports the -NoNewline parameter (introduced in v2), which is of no help here, so the .NET framework must be used:

powershell -Command "[IO.File]::WriteAllText(\"$PWD/file2.txt\", ((gc $PWD/file1.txt) -join \"`n\") + \"`n\")"

Note the need to use path prefix $PWD explicitly, because the .NET Framework's current directory typically differs from PowerShell's.

Also, the output file's encoding will be UTF-8, without a BOM, but you can pass a different encoding as the 3rd argument to [IO.File]::WriteAllText(), such as [System.Text.Encoding]::Default to match Set-Content's default behavior (as of Windows PowerShell v5.1).


Optional reading: platform-specific line breaks "`n" vs. "`r`n" vs. [Environment]::Newline

Implicit choice of newlines (line breaks):

  • when reading, PowerShell accepts LF-only (Unix-style) and CRLF (Windows-style) newlines (line breaks) interchangeably.

  • when writing (e.g., when sending an array of lines / objects to a file with > / Out-File / Set-Content), PowerShell uses the platform-appropriate newline sequence.
    Note, however, that any newline sequences embedded in a given string input object are sent to the file as-is.

As for escape sequences / constants:

  • [Environment]::Newline contains the platform-appropriate newline.

  • "`n" is always just LF (\n)

    • as evidenced by "`n".Length returning 1 and [char] 10 -eq [char] "`n" returning $True (10 is the decimal Unicode/ASCII code point of LF).

    • The documentation isn't explicit about this: Get-Help about_Special_Characters mentions "new line" and "line break", without mentioning what specific character [sequence] that represents. (As mentioned, LF by itself is just as valid a line break as CRLF is in PowerShell).

  • Therefore, to create CRLF sequences, you must use "`r`n".

  • If you need to match newlines in either format, you can use regex '\r?\n', with operators such as -match, -replace, and -split

As for multi-line string literals in scripts (including here-documents):

  • They reflect their source file's newline style. That is, if your script uses CRLF newlines, so will the newlines embedded in the multi-line string.

Solution 2:

Here's one way to do it from the PowerShell command line:

(Get-Content Input.txt -Raw) -replace "`r`n","`n" | Out-File Output.txt -Encoding ASCII -NoNewline

As others have noted this is a PowerShell v5 solution (-Raw appeared in v3, and -NoNewline appeared in v5).

Here's a PowerShell v2 version of the same thing:

$content = [IO.File]::ReadAllText("C:\Path\Input.txt") -replace "`r`n","`n"
[IO.File]::WriteAllText("C:\Path\Output.txt", $content)

(The paths are needed because the .NET methods don't use PowerShell's "current location".)