Out-File
semble forcer la nomenclature lors de l'utilisation de l'UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Comment puis-je écrire un fichier en UTF-8 sans nomenclature à l'aide de PowerShell?
Out-File
semble forcer la nomenclature lors de l'utilisation de l'UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Comment puis-je écrire un fichier en UTF-8 sans nomenclature à l'aide de PowerShell?
Réponses:
Utiliser la UTF8Encoding
classe de .NET et passer $False
au constructeur semble fonctionner:
$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)
suffit. Cette WriteAllLines
surcharge écrit exactement UTF8 sans BOM.
WriteAllLines
semble nécessiter $MyPath
d'être absolu.
WriteAllLines
obtient le répertoire courant de [System.Environment]::CurrentDirectory
. Si vous ouvrez PowerShell, puis modifiez votre répertoire actuel (à l'aide de cd
ou Set-Location
), il [System.Environment]::CurrentDirectory
ne sera pas modifié et le fichier se retrouvera dans le mauvais répertoire. Vous pouvez contourner ce problème en [System.Environment]::CurrentDirectory = (Get-Location).Path
.
La bonne façon pour l'instant est d'utiliser une solution recommandée par @Roman Kuzmin dans les commentaires à @M. Réponse de Dudley :
[IO.File]::WriteAllLines($filename, $content)
(Je l'ai également raccourci un peu en supprimant la System
clarification inutile de l' espace de noms - il sera remplacé automatiquement par défaut.)
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
Je pensais que ce ne serait pas UTF, mais je viens de trouver une solution assez simple qui semble fonctionner ...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
Pour moi, cela se traduit par un utf-8 sans fichier bom quel que soit le format source.
-encoding utf8
pour mon exigence.
-Encoding ASCII
évite le problème de nomenclature, mais vous n'obtenez évidemment que des caractères ASCII 7 bits . Étant donné que l'ASCII est un sous-ensemble de l'UTF-8, le fichier résultant est techniquement également un fichier UTF-8 valide, mais tous les caractères non ASCII de votre entrée seront convertis en ?
caractères littéraux .
-encoding utf8
toujours UTF-8 avec une nomenclature. :(
Remarque: Cette réponse s'applique à Windows PowerShell ; en revanche, dans l' édition multiplateforme PowerShell Core (v6 +), UTF-8 sans BOM est le codage par défaut , sur toutes les applets de commande.
En d'autres termes: si vous utilisez PowerShell [Core] version 6 ou supérieure , vous obtenez des fichiers UTF-8 sans nomenclature par défaut (que vous pouvez également demander explicitement avec -Encoding utf8
/ -Encoding utf8NoBOM
, tandis que vous obtenez avec -BOM avec le codage -utf8BOM
).
Pour compléter la réponse simple et pragmatique de M. Dudley (et la reformulation plus concise de ForNeVeR ):
Pour plus de commodité, voici une fonction avancée Out-FileUtf8NoBom
, une alternative basée sur un pipeline qui imiteOut-File
, ce qui signifie:
Out-File
dans un pipeline.Out-File
.Exemple:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
Notez comment (Get-Content $MyPath)
est inclus (...)
, ce qui garantit que le fichier entier est ouvert, lu en entier et fermé avant d'envoyer le résultat via le pipeline. Ceci est nécessaire pour pouvoir réécrire dans le même fichier (le mettre à jour sur place ).
En général, cependant, cette technique n'est pas recommandée pour 2 raisons: (a) le fichier entier doit tenir en mémoire et (b) si la commande est interrompue, les données seront perdues.
Une note sur l' utilisation de la mémoire :
Code source deOut-FileUtf8NoBom
(également disponible sous forme de Gist sous licence MIT ):
<#
.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()
}
}
À partir de la version 6, powershell prend en charge l' UTF8NoBOM
encodage à la fois pour le contenu défini et le fichier externe et l'utilise même comme encodage par défaut.
Donc, dans l'exemple ci-dessus, cela devrait simplement être comme ceci:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
$PSVersionTable.PSVersion
Lorsque vous utilisez Set-Content
au lieu de Out-File
, vous pouvez spécifier l'encodage Byte
, qui peut être utilisé pour écrire un tableau d'octets dans un fichier. Ceci en combinaison avec un encodage UTF8 personnalisé qui n'émet pas la nomenclature donne le résultat souhaité:
# 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
La différence avec l'utilisation [IO.File]::WriteAllLines()
ou similaire est qu'elle devrait fonctionner correctement avec n'importe quel type d'élément et de chemin, pas seulement les chemins de fichier réels.
Ce script convertira, en UTF-8 sans nomenclature, tous les fichiers .txt dans DIRECTORY1 et les exportera vers DIRECTORY2
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"
}
Source Comment supprimer UTF8 Byte Order Mark (BOM) d'un fichier à l'aide de PowerShell
Si vous souhaitez utiliser [System.IO.File]::WriteAllLines()
, vous devez convertir le deuxième paramètre en String[]
(si le type de $MyFile
est Object[]
) et spécifier également un chemin absolu avec $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
, comme:
$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)
Si vous souhaitez utiliser [System.IO.File]::WriteAllText()
, vous devez parfois | Out-String |
diriger le deuxième paramètre pour ajouter explicitement des CRLF à la fin de chaque ligne (en particulier lorsque vous les utilisez avec 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)
Ou vous pouvez utiliser [Text.Encoding]::UTF8.GetBytes()
avec 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"
voir: Comment écrire le résultat de ConvertTo-Csv dans un fichier en UTF-8 sans BOM
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
is Convert-Path $MyPath
; si vous voulez assurer un CRLF de fin, utilisez simplement [System.IO.File]::WriteAllLines()
même avec une seule chaîne d'entrée (pas besoin de Out-String
).
Une technique que j'utilise consiste à rediriger la sortie vers un fichier ASCII à l'aide de l' applet de commande Out-File .
Par exemple, j'exécute souvent des scripts SQL qui créent un autre script SQL à exécuter dans Oracle. Avec une redirection simple (">"), la sortie sera en UTF-16 qui n'est pas reconnu par SQLPlus. Pour contourner cela:
sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force
Le script généré peut ensuite être exécuté via une autre session SQLPlus sans aucun souci Unicode:
sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
-Encoding ASCII
évite le problème de nomenclature, mais vous n'obtenez évidemment que la prise en charge des caractères ASCII 7 bits . Étant donné que l'ASCII est un sous-ensemble de l'UTF-8, le fichier résultant est techniquement également un fichier UTF-8 valide, mais tous les caractères non ASCII de votre entrée seront convertis en ?
caractères littéraux .
Changez plusieurs fichiers par extension en UTF-8 sans 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)
}
Pour une raison quelconque, les WriteAllLines
appels produisaient toujours une nomenclature pour moi, avec l' UTF8Encoding
argument BOMless et sans lui. Mais ce qui suit a fonctionné pour moi:
$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])
J'ai dû rendre le chemin du fichier absolu pour qu'il fonctionne. Sinon, il a écrit le fichier sur mon bureau. En outre, je suppose que cela ne fonctionne que si vous savez que votre nomenclature est de 3 octets. Je ne sais pas à quel point il est fiable de s'attendre à un format / longueur de nomenclature donné basé sur l'encodage.
En outre, comme écrit, cela ne fonctionne probablement que si votre fichier s'inscrit dans un tableau PowerShell, qui semble avoir une limite de longueur d'une valeur inférieure à celle [int32]::MaxValue
de ma machine.
WriteAllLines
sans argument de codage n'écrit jamais une nomenclature elle - même , mais il est concevable que votre chaîne commence par le caractère BOM ( U+FEFF
), qui lors de l'écriture a effectivement créé une nomenclature UTF-8; par exemple: $s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(omettez le [char] 0xfeff +
pour voir qu'aucune nomenclature n'est écrite).
[Environment]::CurrentDirectory = $PWD.ProviderPath
, soit, comme alternative plus générique à votre "$(pwd)\..."
approche (mieux "$pwd\..."
"$($pwd.ProviderPath)\..."
(Join-Path $pwd.ProviderPath ...)
(Convert-Path BOMthetorpedoes.txt)
U+FEFF
abstrait .
Pourrait utiliser ci-dessous pour obtenir UTF8 sans BOM
$MyFile | Out-File -Encoding ASCII
ASCII
n'est pas UTF-8, mais ce n'est pas non plus la page de code ANSI actuelle - vous pensez Default
; ASCII
est vraiment un codage ASCII 7 bits, avec des points de code> = 128 convertis en ?
instances littérales .
-Encoding ASCII
s'agit bien que d'ASCII 7 bits: 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
- le ä
a été translittéré en a ?
. En revanche, -Encoding Default
("ANSI") le conserverait correctement.
Celui-ci fonctionne pour moi (utilisez "Default" au lieu de "UTF8"):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
Le résultat est ASCII sans nomenclature.
Default
encodage, il utilisera la page de codes ANSI actuelle du système, qui n'est pas UTF-8, comme je le demandais.