410

Out-File seems to force the BOM when using UTF-8:

$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath

How can I write a file in UTF-8 with no BOM using PowerShell?

Update 2021

PowerShell has changed a bit since I wrote this question 10 years ago. Check multiple answers below, they have a lot of good information!

6
  • 38
    BOM = Byte-Order Mark. Three chars placed at the beginning of a file (0xEF,0xBB,0xBF) that look like "" Commented Nov 26, 2014 at 16:50
  • 72
    This is incredibly frustrating. Even third party modules get polluted, like trying to upload a file over SSH? BOM! "Yeah, let's corrupt every single file; that sounds like a good idea." -Microsoft. Commented Apr 1, 2015 at 20:48
  • 10
    The default encoding is UTF8NoBOM starting with Powershell version 6.0 learn.microsoft.com/en-us/powershell/module/… Commented Jul 9, 2019 at 14:48
  • 4
    Talk about breaking backwards compatibility... Commented Jan 13, 2020 at 15:31
  • 1
    I feel like it should be noted that while a BOM in a UTF-8 file does make a lot of systems choke, it is explicitly valid in the Unicode UTF-8 spec to include one. Commented Jun 28, 2022 at 14:10

22 Answers 22

321

Using .NET's UTF8Encoding class and passing $False to the constructor seems to work:

$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)
Sign up to request clarification or add additional context in comments.

15 Comments

Ugh, I hope that's not the only way.
One line [System.IO.File]::WriteAllLines($MyPath, $MyFile) is enough. This WriteAllLines overload writes exactly UTF8 without BOM.
Note that WriteAllLines seems to require $MyPath to be absolute.
@xdhmoore WriteAllLines gets the current directory from [System.Environment]::CurrentDirectory. If you open PowerShell and then change your current directory (using cd or Set-Location), then [System.Environment]::CurrentDirectory will not be changed and the file will end up being in the wrong directory. You can work around this by [System.Environment]::CurrentDirectory = (Get-Location).Path.
|
121

The proper way as of now is to use a solution recommended by @Roman Kuzmin in comments to @M. Dudley answer:

[IO.File]::WriteAllLines($filename, $content)

(I've also shortened it a bit by stripping unnecessary System namespace clarification - it will be substituted automatically by default.)

8 Comments

This (for whatever reason) did not remove the BOM for me, where as the accepted answer did
@Liam, probably some old version of PowerShell or .NET?
I believe older versions of the .NET WriteAllLines function did write the BOM by default. So it could be a version issue.
Confirmed with writes with a BOM in Powershell 3, but without a BOM in Powershell 4. I had to use M. Dudley's original answer.
So it works on Windows 10 where it's installed by default. :) Also, suggested improvement: [IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
|
86

I figured this wouldn't be UTF, but I just found a pretty simple solution that seems to work...

Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext

For me this results in a utf-8 without bom file regardless of the source format.

7 Comments

This worked for me, except I used -encoding utf8 for my requirement.
Thank you very much. I am working with dump logs of a tool - which had tabs inside it. UTF-8 was not working. ASCII solved the problem. Thanks.
Yes, -Encoding ASCII avoids the BOM problem, but you obviously only get 7-bit ASCII characters. Given that ASCII is a subset of UTF-8, the resulting file is technically also a valid UTF-8 file, but all non-ASCII characters in your input will be converted to literal ? characters.
Warning: Definitely not. This deletes all non-ASCII characters and replaces them with question marks. Don't do this or you will lose data! (Tried with PS 5.1 on Windows 10)
10000% agree with @ygoe. This solution should be avoided. There are much better answers here, such as the accepted answer--and of course, the fact that newer versions of the x-plat PowerShell use no BOM by default; but for those using Desktop Edition, see the accepted answer.
|
74

Note: This answer applies to Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1); by contrast, in the cross-platform PowerShell (Core) 7 edition, UTF-8 without BOM is the default encoding, across all cmdlets.

  • In other words: If you're using PowerShell (Core) 7, i.e. version v7.x, you get BOM-less UTF-8 files by default (which you can also explicitly request with -Encoding utf8 / -Encoding utf8NoBOM, whereas you get with-BOM encoding with -utf8BOM).

  • If you're running Windows 10 or above and you're willing to switch to BOM-less UTF-8 encoding system-wide - which has far-reaching consequences, however - even Windows PowerShell can be made to use BOM-less UTF-8 (mostly) consistently - see this answer.


To complement M. Dudley's own simple and pragmatic answer (and ForNeVeR's more concise reformulation):

  • A simple, (non-streaming) PowerShell-native alternative is to use New-Item, which (curiously) creates BOM-less UTF-8 files by default even in Windows PowerShell:

    # Note the use of -Raw to read the file as a whole.
    # Unlike with Set-Content / Out-File *no* trailing newline is appended.
    $null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
    
    • Note: To save the output from arbitrary commands in the same format as Out-File would, pipe to Out-String first; e.g.:

       $null = New-Item -Force Out.txt -Value (Get-ChildItem | Out-String) 
      
  • For convenience, below is advanced custom function Out-FileUtf8NoBom, a pipeline-based alternative that mimics Out-File, which means:

    • you can use it just like Out-File in a pipeline.
    • input objects that aren't strings are formatted as they would be if you sent them to the console, just like with Out-File.
    • an additional -UseLF switch allows you use Unix-format LF-only newlines ("`n") instead of the Windows-format CRLF newlines ("`r`n") you normally get.

Example:

(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath # Add -UseLF for Unix newlines

Note how (Get-Content $MyPath) is enclosed in (...), which ensures that the entire file is opened, read in full, and closed before sending the result through the pipeline. This is necessary in order to be able to write back to the same file (update it in place).
Generally, though, this technique is not advisable for 2 reasons: (a) the whole file must fit into memory and (b) if the command is interrupted, data will be lost.

A note on memory use:

  • M. Dudley's own answer and the New-Item alternative above require that the entire file contents be built up in memory first, which can be problematic with large input sets.
  • The function below does not require this, because it is implemented as a proxy (wrapper) function (for a concise summary of how to define such functions, see this answer).

Source code of function Out-FileUtf8NoBom:

Note:

  • The function is also available as an MIT-licensed Gist, and only the latter will be maintained going forward.

  • iRon7 has since published module WindowsOutFilePatch, which contains a replacement for the Out-File cmdlet in Windows PowerShell that supports the normally PowerShell 7-only utf8BOM and utf8NoBOM -Encoding specifiers, for cross-edition consistency; you can install it with, e.g.,
    Install-Module WindowsOutFilePatch -AllowClobber -Scope CurrentUser and then use Out-File -Encoding utf8NoBOM in Windows PowerShell too.

You can install it directly with the following command (while I can personally assure you that doing so is safe, you should always check the content of a script before directly executing it this way):

# Download and define the function.
irm https://gist.github.com/mklement0/8689b9b5123a9ba11df7214f82a673be/raw/Out-FileUtf8NoBom.ps1 | iex
function Out-FileUtf8NoBom {

  <#
  .SYNOPSIS
    Outputs to a UTF-8-encoded file *without a BOM* (byte-order mark).

  .DESCRIPTION

    Mimics the most important aspects of Out-File:
      * Input objects are sent to Out-String first.
      * -Append allows you to append to an existing file, -NoClobber prevents
        overwriting of an existing file.
      * -Width allows you to specify the line width for the text representations
        of input objects that aren't strings.
    However, it is not a complete implementation of all Out-File parameters:
      * Only a literal output path is supported, and only as a parameter.
      * -Force is not supported.
      * Conversely, an extra -UseLF switch is supported for using LF-only newlines.

  .NOTES
    The raison d'être for this advanced function is that Windows PowerShell
    lacks the ability to write UTF-8 files without a BOM: using -Encoding UTF8 
    invariably prepends a BOM.

    Copyright (c) 2017, 2022 Michael Klement <[email protected]> (http://same2u.net), 
    released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).

  #>

  [CmdletBinding(PositionalBinding=$false)]
  param(
    [Parameter(Mandatory, Position = 0)] [string] $LiteralPath,
    [switch] $Append,
    [switch] $NoClobber,
    [AllowNull()] [int] $Width,
    [switch] $UseLF,
    [Parameter(ValueFromPipeline)] $InputObject
  )

  begin {

    # Convert the input path to a full one, since .NET's working dir. usually
    # differs from PowerShell's.
    $dir = Split-Path -LiteralPath $LiteralPath
    if ($dir) { $dir = Convert-Path -ErrorAction Stop -LiteralPath $dir } else { $dir = $pwd.ProviderPath }
    $LiteralPath = [IO.Path]::Combine($dir, [IO.Path]::GetFileName($LiteralPath))
    
    # If -NoClobber was specified, throw an exception if the target file already
    # exists.
    if ($NoClobber -and (Test-Path $LiteralPath)) {
      Throw [IO.IOException] "The file '$LiteralPath' already exists."
    }
    
    # Create a StreamWriter object.
    # Note that we take advantage of the fact that the StreamWriter class by default:
    # - uses UTF-8 encoding
    # - without a BOM.
    $sw = New-Object System.IO.StreamWriter $LiteralPath, $Append
    
    $htOutStringArgs = @{}
    if ($Width) { $htOutStringArgs += @{ Width = $Width } }

    try { 
      # Create the script block with the command to use in the steppable pipeline.
      $scriptCmd = { 
        & Microsoft.PowerShell.Utility\Out-String -Stream @htOutStringArgs | 
          . { process { if ($UseLF) { $sw.Write(($_ + "`n")) } else { $sw.WriteLine($_) } } }
      }  
      
      $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
      $steppablePipeline.Begin($PSCmdlet)
    }
    catch { throw }

  }

  process
  {
    $steppablePipeline.Process($_)
  }

  end {
    $steppablePipeline.End()
    $sw.Dispose()
  }

}

6 Comments

Example usage from a utf8BOM file to plain utf8: $null = New-Item -Force "\$env:ProgramData\ssh\administrators_authorized_keys" -Value (Get-Content -Path "\$env:ProgramData\ssh\administrators_authorized_keys" | Out-String)
@nhooyr, it's better to use $null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath) (much faster, and preserves the existing newline format) - I've updated the answer.
New-Item -Force $MyPath -Value (Get-Content [file list] | Out-String) was better for me because it combines files with mixed newlines or even some trailing or no newlines properly. If not handling large amounts of data, it works best.
-Encoding utf8 added the BOM in my case. I used -Encoding Default instead to convert to UTF-8 without BOM (Windows 11)
@MaxM: While In Windows PowerShell -Encoding Default indeed creates files without a BOM it does not create UTF-8 files: instead, it uses the system's legacy locale's ANSI code page, such as Windows-1252 on US-English systems. Such legacy, fixed-single-byte encodings are incompatible with UTF-8, so any content comprising non-ASCII-range characters encoded this way will be misinterpreted if read as UTF-8.
|
37

Starting from version 6 powershell supports the UTF8NoBOM encoding both for set-content and out-file and even uses this as default encoding.

So in the above example it should simply be like this:

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath

4 Comments

Nice. FYI check version with $PSVersionTable.PSVersion
Worth noting that in PowerShell [Core] v6+ -Encoding UTF8NoBOM is never required, because it is the default encoding.
learn.microsoft.com/en-us/powershell/module/… seems to confirm this - but having issues scraping redirected dos output to files and emitting back out. Notepad++ gives different encodings (UCS-2 LE BOM on one, and UTF8-BOM on the other!) on two of files I emit - when driving a screen scraping automation/redirection to an temp outfile and extracting substrings. I am driving from a CSV for different arguments for the same command, also setting encoding everywhere - but Powershell seems to be ignoring that.
Be sure to check out this answer and my comment under the question itself here too if you are pulling your hair out over Powershell encoding not working as expected (Set-Content and Out-File giving different encoding answers despite specifically requesting utf8!). stackoverflow.com/a/5596984/495157
23

Here's an alternative to the accepted answer.

The advantage of this approach is that it's compatible with IO.FileInfo objects (from functions like Get-Item) and relative paths.

  1. Create a Text.UTF8Encoding object

    • While Text.UTF8Encoding is capable of inserting a BOM, it doesn't by default
  2. Call the object's GetBytes method to convert a string into bytes

    • Ensure the target string isn't actually a string array$stringVar.Count should equal 1
  3. Write the byte array to your target with Set-Content -Encoding Byte

# This is a reusable class instance object
$utf8 = New-Object Text.UTF8Encoding

$GCRaw = Get-Content -Raw -PSPath $MyPath
Set-Content -Value $utf8.GetBytes($GCRaw) -Encoding Byte -PSPath $MyPath

This can be shortened by letting -Value be inferred by position and, additionally, by creating the Text.UTF8Encoding object from within the argument.

$GCRaw = Get-Content $MyPath -Raw

Set-Content ([Text.UTF8Encoding]::new().GetBytes($GCRaw)) -Encoding Byte -PSPath $MyPath

#NOTE#
# (New-Object Text.UTF8Encoding).GetBytes($GCRaw))
# can be used instead of
# ([Text.UTF8Encoding]::new().GetBytes($GCRaw))
# For code intended to be compact, I recommend the latter,
# not just because it's not as long, but also because its
# lack of whitespace makes it visually more distinct.

1 Comment

Nice - works great with strings (which may be all that is needed and certainly meets the requirements of the question). In case you need to take advantage of the formatting that Out-File, unlike Set-Content, provides, pipe to Out-String first; e.g., $MyFile = Get-ChildItem | Out-String
10

important!: this only works if an extra space or newline at the start is no problem for your use case of the file
(e.g. if it is an SQL file, Java file or human readable text file)

one could use a combination of creating an empty (non-UTF8 or ASCII (UTF8-compatible)) file and appending to it (replace $str with gc $src if the source is a file):

" "    |  out-file  -encoding ASCII  -noNewline  $dest
$str  |  out-file  -encoding UTF8   -append     $dest

as one-liner

replace $dest and $str according to your use case:

$_ofdst = $dest ; " " | out-file -encoding ASCII -noNewline $_ofdst ; $src | out-file -encoding UTF8 -append $_ofdst

as simple function

function Out-File-UTF8-noBOM { param( $str, $dest )
  " "    |  out-file  -encoding ASCII  -noNewline  $dest
  $str  |  out-file  -encoding UTF8   -append     $dest
}

using it with a source file:

Out-File-UTF8-noBOM  (gc $src),  $dest

using it with a string:

Out-File-UTF8-noBOM  $str,  $dest
  • optionally: continue appending with Out-File:

    "more foo bar"  |  Out-File -encoding UTF8 -append  $dest
    

2 Comments

UTF8 != ASCII. So no, this wouldn't work in all cases. All ASCII can be converted to UTF8, but not ALL UTF8 can be converted to ASCII and back again--i.e., UTF8 is NOT (all) round-trippable to ASCII and back. In fact, this can be dangerous.
@fourpastmidnight that's why I mentioned this in the beginning :)
9

Old question, new answer:

While the "old" powershell writes a BOM, the new platform-agnostic variant does behave differently: The default is "no BOM" and it can be configured via switch:

-Encoding

Specifies the type of encoding for the target file. The default value is utf8NoBOM.

The acceptable values for this parameter are as follows:

  • ascii: Uses the encoding for the ASCII (7-bit) character set.
  • bigendianunicode: Encodes in UTF-16 format using the big-endian byte order.
  • oem: Uses the default encoding for MS-DOS and console programs.
  • unicode: Encodes in UTF-16 format using the little-endian byte order.
  • utf7: Encodes in UTF-7 format.
  • utf8: Encodes in UTF-8 format.
  • utf8BOM: Encodes in UTF-8 format with Byte Order Mark (BOM)
  • utf8NoBOM: Encodes in UTF-8 format without Byte Order Mark (BOM)
  • utf32: Encodes in UTF-32 format.

Source: https://learn.microsoft.com/de-de/powershell/module/Microsoft.PowerShell.Utility/Out-File?view=powershell-7 Emphasis mine

Comments

7

This script will convert, to UTF-8 without BOM, all .txt files in DIRECTORY1 and output them to DIRECTORY2

foreach ($i in ls -name DIRECTORY1\*.txt)
{
    $file_content = Get-Content "DIRECTORY1\$i";
    [System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}

2 Comments

This one fails without any warning. What version of powershell should I use to run it?
The WriteAllLines solution works great for small files. However, I need a solution for larger files. Every time I try to use this with a larger file I'm getting an OutOfMemory error.
5

For PowerShell 5.1, enable this setting:

Control Panel, Region, Administrative, Change system locale, Use Unicode UTF-8 for worldwide language support

Then enter this into PowerShell:

$PSDefaultParameterValues['*:Encoding'] = 'Default'

Alternatively, you can upgrade to PowerShell 6 or higher.

https://github.com/PowerShell/PowerShell

1 Comment

To spell it out: This is a system-wide setting that makes Windows PowerShell default to BOM-less UTF-8 across all cmdlets, which may or may not be desired, not least because the feature is still in beta (as of this writing) and can break legacy console applications - see this answer for background information.
4

If you want to use [System.IO.File]::WriteAllLines(), you should cast second parameter to String[] (if the type of $MyFile is Object[]), and also specify absolute path with $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath), like:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Set-Variable MyFile
[System.IO.File]::WriteAllLines($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath), [String[]]$MyFile, $Utf8NoBomEncoding)

If you want to use [System.IO.File]::WriteAllText(), sometimes you should pipe the second parameter into | Out-String | to add CRLFs to the end of each line explictly (Especially when you use them with ConvertTo-Csv):

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Out-String | Set-Variable tmp
[System.IO.File]::WriteAllText("/absolute/path/to/foobar.csv", $tmp, $Utf8NoBomEncoding)

Or you can use [Text.Encoding]::UTF8.GetBytes() with Set-Content -Encoding Byte:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Out-String | % { [Text.Encoding]::UTF8.GetBytes($_) } | Set-Content -Encoding Byte -Path "/absolute/path/to/foobar.csv"

see: How to write result of ConvertTo-Csv to a file in UTF-8 without BOM

1 Comment

Good pointers; suggestions/: the simpler alternative to $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath) is Convert-Path $MyPath; if you want to ensure a trailing CRLF, simply use [System.IO.File]::WriteAllLines() even with a single input string (no need for Out-String).
3
    [System.IO.FileInfo] $file = Get-Item -Path $FilePath 
    $sequenceBOM = New-Object System.Byte[] 3 
    $reader = $file.OpenRead() 
    $bytesRead = $reader.Read($sequenceBOM, 0, 3) 
    $reader.Dispose() 
    #A UTF-8+BOM string will start with the three following bytes. Hex: 0xEF0xBB0xBF, Decimal: 239 187 191 
    if ($bytesRead -eq 3 -and $sequenceBOM[0] -eq 239 -and $sequenceBOM[1] -eq 187 -and $sequenceBOM[2] -eq 191) 
    { 
        $utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False) 
        [System.IO.File]::WriteAllLines($FilePath, (Get-Content $FilePath), $utf8NoBomEncoding) 
        Write-Host "Remove UTF-8 BOM successfully" 
    } 
    Else 
    { 
        Write-Warning "Not UTF-8 BOM file" 
    }  

Source How to remove UTF8 Byte Order Mark (BOM) from a file using PowerShell

Comments

3

I have created the following code for easy logging. It creates an UTF-8 file without BOM when executed in PowerShell 5. It works as expected for me. Feel free to customize it to your needs :-)

Function myWriteLog{
# $LogFilePath has to be defined before calling the function 
# And, "$SciptName=$MyInvocation.MyCommand.Name" has to be set before calling the function 
    Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
           [string]$content
         )

# disallow a NULL or an EMPTY value 
if ([string]::IsNullOrEmpty($content.Trim())){
    throw "Found 'EMPTY or NULL': you must provide a nonNull and nonEmpty string to function ""myWriteLog"""
    return 0
} else { 
    if((Test-Path $LogFilePath) -eq $False){

        # Creates the file, please note that option  "-NoNewline" has to be set
        "" | Out-file -FilePath $LogFilePath -Encoding ascii -Force -NoNewline
    
        # Create a string as a line separator for a file header
        $t ="".PadLeft(("Logfile for : $SciptName").Length,"#")
        Add-Content -path $LogFilePath -value "$t"
        Add-Content -path $LogFilePath -value "Logfile for : $SciptName"
        Add-Content -path $LogFilePath -value "LogFile Created: $(Get-date -F "yyyy-MM-dd-HH-mm-ss")"
        Add-Content -path $LogFilePath -value "$t"
        Add-Content -path $LogFilePath -value ""
        
        #and now add the content to 
        Add-Content -path $LogFilePath -value "$(Get-date -F "yyyy-MM-dd-HH-mm-ss") : $content" -Encoding UTF8 -force
    }else{
        Add-Content -path $LogFilePath -value "$(Get-date -F "yyyy-MM-dd-HH-mm-ss") : $content" -Encoding UTF8 -force
    }
}

}

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
2

Change multiple files by extension to UTF-8 without BOM:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in ls -recurse -filter "*.java") {
    $MyFile = Get-Content $i.fullname 
    [System.IO.File]::WriteAllLines($i.fullname, $MyFile, $Utf8NoBomEncoding)
}

Comments

2

One technique I utilize is to redirect output to an ASCII file using the Out-File cmdlet.

For example, I often run SQL scripts that create another SQL script to execute in Oracle. With simple redirection (">"), the output will be in UTF-16 which is not recognized by SQLPlus. To work around this:

sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force

The generated script can then be executed via another SQLPlus session without any Unicode worries:

sqlplus / as sysdba "@new_script.sql" |
tee new_script.log

Update: As others have pointed out, this will drop non-ASCII characters. Since the user asked for a way to "force" conversion, I assume they do not care about that as perhaps their data does not contain such data.

If you care about the preservation of non-ASCII characters, this is not the answer for you.

5 Comments

Yes, -Encoding ASCII avoids the BOM problem, but you obviously only get support for 7-bit ASCII characters. Given that ASCII is a subset of UTF-8, the resulting file is technically also a valid UTF-8 file, but all non-ASCII characters in your input will be converted to literal ? characters.
This answer needs more votes. The sqlplus incompatibility with BOM is a cause of many headaches.
@AmitNaidu No, this is the wrong answer, because it won't work if the text has any non-ascii characters: any accents, umlauts, oriental/cryllic, etc.
@JoelCoehoorn This is a correct answer according to what the user asked. Since the user asked for a way to "force", they're not expecting any issues or don't care probably because the source doesn't use any non-ASCII characters. For those who do care about the preservation of those characters, this will not work.
No, the OP did not ask for a way to "force". They said PowerShell out-file forces BOM.
2

I would say to use just the Set-Content command, nothing else needed.

The powershell version in my system is :-

PS C:\Users\XXXXX> $PSVersionTable.PSVersion | fl


Major         : 5
Minor         : 1
Build         : 19041
Revision      : 1682
MajorRevision : 0
MinorRevision : 1682

PS C:\Users\XXXXX>

So you would need something like following.

PS C:\Users\XXXXX> Get-Content .\Downloads\finddate.txt
Thursday, June 23, 2022 5:57:59 PM
PS C:\Users\XXXXX> Get-Content .\Downloads\finddate.txt | Set-Content .\Downloads\anotherfile.txt
PS C:\Users\XXXXX> Get-Content .\Downloads\anotherfile.txt
Thursday, June 23, 2022 5:57:59 PM
PS C:\Users\XXXXX>

Now when we check the file as per the screenshot it is utf8. anotherfile.txt

PS: To answer on the comment query on foreign character issue. The contents from file "testfgnchar.txt" which having the foreign characters, was copied to "findfnchar2.txt" using the following command.

PS C:\Users\XXXXX> Get-Content .\testfgnchar.txt | Set-Content findfnchar2.txt
PS C:\Users\XXXXX>

screen-shot is here.

Note: Currently, there are newer versions of PowerShell exists, than the one I used during answer.

16 Comments

This seemed to work at first, but this actually uses the user’s ANSI code page and replaces other symbols with closest equivalents (e. g. š → s) or question marks. Using set-content -encoding utf8 works.
Ah, that’s true; I didn’t notice. But then that means this command is completely unsuitable for the task, because without -encoding, it doesn’t use UTF-8 at all, neither with BOM nor without.
The point is that this command uses the ANSI code page, not UTF-8, which was explicitly requested in the question (unless you’ve set ANSI to be UTF-8 as per Zombo’s answer). On your English system, try echo āčķʃλшא⁴ℝ→⅛≈あ子 | set-content file.txt, and you’ll see none of the characters were preserved. The same problem is pointed out for other PowerShell command in the comments to other answers. It’s certainly good to know that set-content defaults to encoding Latin in single bytes, but it is very different from UTF-8 as originally requested.
I’m afraid I have no idea what the screenshot illustrates, and you confirm that you cannot produce UTF-8 from the command line. Here’s another test: run echo Naïveté | set-content file.txt on the command line and open the file in Notepad++. It won’t be in UTF-8; it will be in Windows-1252, which is your system’s ANSI encoding.
This is a question about a very clearly defined character encoding, and this answer fails to provide it. It is possible to configure a modern Windows system to use UTF-8 as the ANSI encoding, and Zombo’s answer shows how. But this answer makes no attempt to even recognize this, and it isn’t quite what the question asked for, either, as it is a system-wide setting instead of an individual command.
|
2

If your first line does not contain anything fancy that doesn't require UTF8, the following will create an UTF8 file without BOM on stock Windows 10 Powershell:

$file = get-content -path "C:\temp\myfile.txt" -Encoding UTF8

# do some stuff.

$file[0] | out-file "C:\temp\mynewfile.txt" -Encoding ascii
$file | select -skip 1 | out-file "C:\temp\mynewfile.txt" -append utf8

This uses 2 lines to create the new file. The first one uses -encoding ascii to force UTF8, but it will be limited to 7-bit ascii. With a textfile, this is usually not an issue, otherwise you'd probably choose byte encoding anyway.

The second command appends the rest, but skips the first line as we already parsed that one with full UTF8 support.

Comments

1

Used this method to edit a UTF8-NoBOM file and generated a file with correct encoding-

$fileD = "file.xml"
(Get-Content $fileD) | ForEach-Object { $_ -replace 'replace text',"new text" } | out-file "file.xml" -encoding ASCII

I was skeptical at this method at first, but it surprised me and worked!

Tested with powershell version 5.1

Comments

0

I have the same error in the PowerShell and used this isolation and fixed it

$PSDefaultParameterValues['*:Encoding'] = 'utf8'

7 Comments

Just setting the utf is not enough. You also need to specify -encoding default. eg: $filecontent | Out-File $Filename -Encoding default. Do note that this reencodes the file with utf8, so a file that was not encoded properly will change.
bom is the three byte code at the beginning of a file to indicate the type of a file, and has nothing really to do with the encoding.
In Windows PowerShell, this doesn't address the requirement that UTF-8 files without a BOM be written, which is what this question is about. You'll invariably get UTF-8 files with a BOM. In PowerShell (Core) 7, the problem doesn't arise, because BOM-less UTF-8 is the consistent default encoding.
@LPChip, note that -Encoding default requests ANSI encoding in Windows PowerShell, not UTF-8.
@mklement0 using the -encoding parameter and set it to default, will set it to whatever the $PSDefaultParameterValues... is. If that is set to utf8, no, it will not do ANSI encoding.
|
0

I faced this issue for me, I myself am author of the program (that's more like a mode for the free software, so I can't just rewrite its core) which is sensitive to BOM. I've done some tests and asked a lot of questions. I needed a cmd script that would process one specific text file after it was encoded in utf8 for my program to work correctly in Cyrillic.

The best answer that I myself got is to use something like this:

powershell -Command "(gc '%CD%\myfile.txt') "^
...
"| Out-File -encoding utf8 '%CD%\myfile.txt'"
powershell "(get-content %CD%\myfile.txt -Encoding Byte) | select -skip 3 | set-content %CD%\myfile.txt -Encoding Byte"

By no means do I claim authorship of the method, thanks a lot to js2010 for the hint.

And I think this is good enough. The program wasn't starting at all with BOM, I checked, and now it started in latin directory. But for Cyrillic this didn't work, I think because the program itself don't support utf-8 Cyrillic representation.

The only thing that truly solved my problem was:

chcp 1251
powershell -Command "(gc '%CD%\myfile.txt') "^
...
"| Out-File -encoding default '%CD%\myfile.txt'"

By setting chcp 1251 the program finally understood Cyrillic (it became corrupted for Windows notepad for some reason but perfectly readable for my program), default in this situation returns the previously set value. We have expanded the cmd ASCII to ANSI and removed the BOM. If we need list of additional characters other than Cyrillic we can use chcp 1252 or any other.

I hope this solves your problem.

6 Comments

The BOM-removal code is conceptually straightforward, but quite slow (which may or may not matter in practice, depending on the size of your files). A much faster alternative, though note that it requires reading the entire file into memory in full first (not usually a problem with text files): set "targetFile=%CD%\myfile.txt" & powershell [IO.File]::WriteAllBytes($env:targetFile, [Linq.Enumerable]::Skip((Get-Content -Raw -Encoding Byte -LiteralPath $env:targetFile), 3)). Either way, be sure to only apply this technique to files you know to have a UTF-8 BOM, otherwise you'll corrupt them.
While these byte-removal techniques do work for removing a UTF-8 BOM from a file after the fact, note that the workarounds among the answers could have helped you avoid creating a UTF-8 file with BOM to begin with.
As for your desire to rewrite the file with a different encoding, Windows-1251 (which is unrelated to the question at hand): Calling chcp 1251 before powershell.exe in combination with -Encoding Default does work, though note that you may want to restore the original code page afterwards, so as not to affect other programs. It's also possible to avoid the chcp 1251 call and apply the encoding selectively, natively in PowerShell 7 (Core), and via direct use of .NET APIs in Windows PowerShell - see this answer.
As for "it became corrupted for Windows notepad for some reason": Notepad misinterpreted your input file (which doesn't amount to corruption unless you re-save it), for the following reasons: Notepad, in the absence of a BOM, tries to guess the input file's encoding: If it detects UTF-8, it uses that. Otherwise - namely if it detects bytes with values that wouldn't be valid in UTF-8 - it defaults to the system's active ANSI code page (e.g., Windows-1252 on English/Western European systems), and if that code page differs from Windows-1251, misinterpretation is the result.
If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From Review
|
-3

This one works for me (use "Default" instead of "UTF8"):

$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath

The result is ASCII without BOM.

3 Comments

Per the Out-File documentation specifying the Default encoding will use the system's current ANSI code page, which is not UTF-8, as I required.
This does seem to work for me, at least for Export-CSV. If you open the resulting file in a proper editor, the file encoding is UTF-8 without BOM, and not Western Latin ISO 9 as I would have expected with ASCII
Many editors open the file as UTF-8 if they can't detect the encoding.
-3

Could use below to get UTF8 without BOM

$MyFile | Out-File -Encoding ASCII

6 Comments

No, it will convert the output to current ANSI codepage (cp1251 or cp1252, for example). It is not UTF-8 at all!
Thanks Robin. This may not have worked for writing a UTF-8 file without the BOM but the -Encoding ASCII option removed the BOM. That way I could generate a bat file for gvim. The .bat file was tripping up on the BOM.
@ForNeVeR: You're correct that encoding ASCII is not UTF-8, but it's als not the current ANSI codepage - you're thinking of Default; ASCII truly is 7-bit ASCII encoding, with codepoints >= 128 getting converted to literal ? instances.
@ForNeVeR: You're probably thinking of "ANSI" or "extended ASCII". Try this to verify that -Encoding ASCII is indeed 7-bit ASCII only: 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f) - the ä has been transliterated to a ?. By contrast, -Encoding Default ("ANSI") would correctly preserve it.
@rob This is the perfect answer for everybody who just doesn't need utf-8 or anything else that is different to ASCII and is not interested in understanding encodings and the purpose of unicode. You can use it as utf-8 because the equivalent utf-8 characters to all ASCII characters are identical (means converting an ASCII-file to an utf-8-file results in an identical file (if it gets no BOM)). For all who have non-ASCII characters in their text this answer is just false and misleading.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.