Out-File
scheint die Stückliste bei Verwendung von UTF-8 zu erzwingen:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Wie kann ich mit PowerShell eine Datei in UTF-8 ohne Stückliste schreiben?
Out-File
scheint die Stückliste bei Verwendung von UTF-8 zu erzwingen:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Wie kann ich mit PowerShell eine Datei in UTF-8 ohne Stückliste schreiben?
Antworten:
Die Verwendung der .NET- UTF8Encoding
Klasse und die Übergabe $False
an den Konstruktor scheint zu funktionieren:
$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)
[System.IO.File]::WriteAllLines($MyPath, $MyFile)
reicht aus. Diese WriteAllLines
Überladung schreibt genau UTF8 ohne Stückliste.
WriteAllLines
scheint $MyPath
.
WriteAllLines
das aktuelle Verzeichnis ab [System.Environment]::CurrentDirectory
. Wenn Sie PowerShell öffnen und dann Ihr aktuelles Verzeichnis ändern (mit cd
oder Set-Location
), [System.Environment]::CurrentDirectory
wird dies nicht geändert und die Datei befindet sich im falschen Verzeichnis. Sie können dies umgehen, indem Sie [System.Environment]::CurrentDirectory = (Get-Location).Path
.
Der richtige Weg ist ab sofort, eine von @Roman Kuzmin in Kommentaren zu @M empfohlene Lösung zu verwenden. Dudley Antwort :
[IO.File]::WriteAllLines($filename, $content)
(Ich habe es auch ein wenig verkürzt, indem ich unnötige System
Namespace-Klarstellungen entfernt habe - es wird standardmäßig automatisch ersetzt.)
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
Ich dachte, das wäre kein UTF, aber ich habe gerade eine ziemlich einfache Lösung gefunden, die zu funktionieren scheint ...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
Für mich führt dies zu einem utf-8 ohne bom-Datei, unabhängig vom Quellformat.
-encoding utf8
für meine Anforderung verwendet.
-Encoding ASCII
vermeidet das Stücklistenproblem, aber Sie erhalten offensichtlich nur 7-Bit-ASCII-Zeichen . Da ASCII eine Teilmenge von UTF-8 ist, ist die resultierende Datei technisch auch eine gültige UTF-8 - Datei, aber alle Nicht-ASCII - Zeichen in Ihrer Eingabe werden wörtliche umgewandelt werden ?
Zeichen .
-encoding utf8
immer noch UTF-8 mit einer Stückliste aus. :(
Hinweis: Diese Antwort gilt für Windows PowerShell . Im Gegensatz dazu ist in der plattformübergreifenden PowerShell Core Edition (Version 6 +) UTF-8 ohne Stückliste die Standardcodierung für alle Cmdlets.
Mit anderen Worten: Wenn Sie PowerShell [Core] Version 6 oder höher verwenden , erhalten Sie standardmäßig Stücklistenlose UTF-8-Dateien (die Sie auch explizit mit -Encoding utf8
/ anfordern können -Encoding utf8NoBOM
, während Sie mit -BOM-Codierung mit erhalten -utf8BOM
).
Zur Ergänzung von M. Dudleys eigener einfacher und pragmatischer Antwort (und der präziseren Neuformulierung von ForNeVeR) ):
Der Einfachheit halber ist hier die erweiterte Funktion Out-FileUtf8NoBom
, eine Pipeline-basierte Alternative, die nachahmtOut-File
, was bedeutet:
Out-File
in einer Pipeline verwenden.Out-File
.Beispiel:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
Beachten Sie, wie (Get-Content $MyPath)
eingeschlossen ist (...)
, wodurch sichergestellt wird, dass die gesamte Datei geöffnet, vollständig gelesen und geschlossen wird, bevor das Ergebnis über die Pipeline gesendet wird. Dies ist erforderlich, um in dieselbe Datei zurückschreiben zu können (aktualisieren Sie sie an Ort und Stelle ).
Im Allgemeinen ist diese Technik jedoch aus zwei Gründen nicht ratsam: (a) Die gesamte Datei muss in den Speicher passen, und (b) wenn der Befehl unterbrochen wird, gehen Daten verloren.
Ein Hinweis zur Speichernutzung :
Quellcode vonOut-FileUtf8NoBom
(auch als MIT-lizenzierte Gist erhältlich ):
<#
.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-String parameters:
* Only a literal output path is supported, and only as a parameter.
* -Force is not supported.
Caveat: *All* pipeline input is buffered before writing output starts,
but the string representations are generated and written to the target
file one by one.
.NOTES
The raison d'être for this advanced function is that, as of PowerShell v5,
Out-File still lacks the ability to write UTF-8 files without a BOM:
using -Encoding UTF8 invariably prepends a BOM.
#>
function Out-FileUtf8NoBom {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position=0)] [string] $LiteralPath,
[switch] $Append,
[switch] $NoClobber,
[AllowNull()] [int] $Width,
[Parameter(ValueFromPipeline)] $InputObject
)
#requires -version 3
# Make sure that the .NET framework sees the same working dir. as PS
# and resolve the input path to a full path.
[System.IO.Directory]::SetCurrentDirectory($PWD.ProviderPath) # Caveat: Older .NET Core versions don't support [Environment]::CurrentDirectory
$LiteralPath = [IO.Path]::GetFullPath($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 IO.StreamWriter $LiteralPath, $Append
$htOutStringArgs = @{}
if ($Width) {
$htOutStringArgs += @{ Width = $Width }
}
# Note: By not using begin / process / end blocks, we're effectively running
# in the end block, which means that all pipeline input has already
# been collected in automatic variable $Input.
# We must use this approach, because using | Out-String individually
# in each iteration of a process block would format each input object
# with an indvidual header.
try {
$Input | Out-String -Stream @htOutStringArgs | % { $sw.WriteLine($_) }
} finally {
$sw.Dispose()
}
}
Ab Version 6 unterstützt Powershell die UTF8NoBOM
Codierung sowohl für Set-Content als auch für Out-File und verwendet diese sogar als Standardcodierung.
Im obigen Beispiel sollte es einfach so sein:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
$PSVersionTable.PSVersion
Bei Verwendung von Set-Content
anstelle von Out-File
können Sie die Codierung angeben Byte
, mit der ein Byte-Array in eine Datei geschrieben werden kann. Dies in Kombination mit einer benutzerdefinierten UTF8-Codierung, die die Stückliste nicht ausgibt, ergibt das gewünschte Ergebnis:
# This variable can be reused
$utf8 = New-Object System.Text.UTF8Encoding $false
$MyFile = Get-Content $MyPath -Raw
Set-Content -Value $utf8.GetBytes($MyFile) -Encoding Byte -Path $MyPath
Der Unterschied zur Verwendung [IO.File]::WriteAllLines()
oder ähnlichem besteht darin, dass es mit jeder Art von Element und Pfad gut funktionieren sollte, nicht nur mit tatsächlichen Dateipfaden.
Dieses Skript konvertiert alle TXT-Dateien in DIRECTORY1 in UTF-8 ohne Stückliste und gibt sie in DIRECTORY2 aus
foreach ($i in ls -name DIRECTORY1\*.txt)
{
$file_content = Get-Content "DIRECTORY1\$i";
[System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}
[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"
}
Quelle Entfernen von UTF8-Byte-Bestellmarken (BOM) aus einer Datei mit PowerShell
Wenn Sie verwenden möchten [System.IO.File]::WriteAllLines()
, sollten Sie den zweiten Parameter in String[]
(wenn der Typ von $MyFile
ist Object[]
) umwandeln und auch den absoluten Pfad mit angeben $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
, wie:
$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)
Wenn Sie verwenden möchten [System.IO.File]::WriteAllText()
, sollten Sie manchmal den zweiten Parameter | Out-String |
einfügen, um CRLFs explizit am Ende jeder Zeile hinzuzufügen (insbesondere, wenn Sie sie mit verwenden 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)
Oder Sie können verwenden [Text.Encoding]::UTF8.GetBytes()
mit 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"
Siehe: So schreiben Sie das Ergebnis von ConvertTo-Csv in eine Datei in UTF-8 ohne Stückliste
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
ist Convert-Path $MyPath
; Wenn Sie eine nachfolgende CRLF sicherstellen möchten, verwenden Sie einfach [System.IO.File]::WriteAllLines()
auch eine einzelne Eingabezeichenfolge (keine Notwendigkeit Out-String
).
Eine Technik, die ich verwende, besteht darin, die Ausgabe mithilfe des Cmdlets Out-File in eine ASCII -Datei umzuleiten.
Zum Beispiel führe ich häufig SQL-Skripte aus, die ein anderes SQL-Skript erstellen, das in Oracle ausgeführt werden soll. Bei einfacher Umleitung (">") erfolgt die Ausgabe in UTF-16, das von SQLPlus nicht erkannt wird. Um dies zu umgehen:
sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force
Das generierte Skript kann dann ohne Unicode-Probleme über eine andere SQLPlus-Sitzung ausgeführt werden:
sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
-Encoding ASCII
vermeidet das Stücklistenproblem, aber Sie erhalten offensichtlich nur Unterstützung für 7-Bit-ASCII-Zeichen . Da ASCII eine Teilmenge von UTF-8 ist, ist die resultierende Datei technisch auch eine gültige UTF-8 - Datei, aber alle Nicht-ASCII - Zeichen in Ihrer Eingabe werden wörtliche umgewandelt werden ?
Zeichen .
Ändern Sie mehrere Dateien durch Erweiterung in UTF-8 ohne Stückliste:
$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)
}
Aus irgendeinem Grund produzierten die WriteAllLines
Anrufe immer noch eine Stückliste für mich, mit dem UTF8Encoding
Argument Stücklistenlos und ohne. Aber das Folgende hat bei mir funktioniert:
$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])
Ich musste den Dateipfad absolut machen, damit er funktioniert. Andernfalls wurde die Datei auf meinen Desktop geschrieben. Außerdem funktioniert dies vermutlich nur, wenn Sie wissen, dass Ihre Stückliste 3 Byte umfasst. Ich habe keine Ahnung, wie zuverlässig es ist, ein bestimmtes Stücklistenformat / eine bestimmte Stücklistenlänge basierend auf der Codierung zu erwarten.
Wie geschrieben, funktioniert dies wahrscheinlich nur, wenn Ihre Datei in ein Powershell-Array passt, dessen Längenbegrenzung um einen Wert niedriger zu sein scheint als [int32]::MaxValue
auf meinem Computer.
WriteAllLines
ohne eine Codierung Argument schreibt nie eine Stückliste selbst , aber es ist denkbar , dass Ihre Zeichenfolge mit der BOM starten passierten Zeichen ( U+FEFF
), die effektiv erstellt einen UTF-8 BOM auf dem Schreiben; zB: $s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(lassen Sie das weg, um [char] 0xfeff +
zu sehen, dass keine Stückliste geschrieben ist).
[Environment]::CurrentDirectory = $PWD.ProviderPath
oder als allgemeinere Alternative zu Ihrem "$(pwd)\..."
Ansatz (besser : "$pwd\..."
, noch besser: "$($pwd.ProviderPath)\..."
oder (Join-Path $pwd.ProviderPath ...)
)(Convert-Path BOMthetorpedoes.txt)
U+FEFF
.
Könnte unten verwenden, um UTF8 ohne Stückliste zu erhalten
$MyFile | Out-File -Encoding ASCII
ASCII
nicht UTF-8 ist, aber es ist auch nicht die aktuelle ANSI-Codepage - Sie denken daran Default
; ASCII
Es handelt sich tatsächlich um eine 7-Bit-ASCII-Codierung, bei der Codepunkte> = 128 in Literalinstanzen konvertiert ?
werden.
-Encoding ASCII
es sich tatsächlich nur um 7-Bit-ASCII handelt: 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
- Das ä
wurde in a transkribiert ?
. Im Gegensatz dazu würde -Encoding Default
("ANSI") es korrekt beibehalten.
Dieser funktioniert für mich (verwenden Sie "Standard" anstelle von "UTF8"):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
Das Ergebnis ist ASCII ohne Stückliste.
Default
Codierung angegeben ist, wird die aktuelle ANSI-Codepage des Systems verwendet, die nicht wie erforderlich UTF-8 ist.