PowerShell을 사용하여 BOM없이 UTF-8로 파일 작성


246

Out-File UTF-8을 사용할 때 BOM을 강제하는 것 같습니다.

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

PowerShell을 사용하여 BOM없이 UTF-8로 파일을 작성하려면 어떻게해야합니까?


23
BOM = 바이트 주문 마크. ""와 같은 파일 (0xEF, 0xBB, 0xBF)의 시작 부분에 배치 된 3 개의 문자
Signal15

40
이것은 매우 실망입니다. SSH를 통해 파일을 업로드하는 것과 같이 타사 모듈도 오염됩니까? BOM! "예, 모든 단일 파일을 손상 시키십시오. 좋은 생각 인 것 같습니다." 마이크로 소프트
MichaelGG

3
기본 인코딩은 Powershell 버전 6.0부터 시작하는 UTF8NoBOM입니다. docs.microsoft.com/en-us/powershell/module/…
Paul Shiryaev

이전 버전과의 호환성을 깨는 것에 대해 이야기하십시오 ...
Dragas

답변:


220

.NET UTF8Encoding클래스를 사용 $False하고 생성자에 전달 하면 작동하는 것 같습니다.

$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)

42
어, 그게 유일한 방법이 아니길 바랍니다.
Scott Muc

114
한 줄이면 [System.IO.File]::WriteAllLines($MyPath, $MyFile)충분합니다. 이 WriteAllLines과부하는 BOM없이 정확하게 UTF8을 씁니다.
Roman Kuzmin


3
절대적이어야 할 WriteAllLines것 같습니다 $MyPath.
sschuberth

10
@xdhmoore WriteAllLines는에서 현재 디렉토리를 가져옵니다 [System.Environment]::CurrentDirectory. 당신이 PowerShell을 열고 현재 디렉토리 (사용 변경하는 경우 cd또는 Set-Location), 다음 [System.Environment]::CurrentDirectory변경되지 않습니다 및 파일이 잘못된 디렉토리에 끝나게됩니다. 에 의해이 문제를 해결할 수 있습니다 [System.Environment]::CurrentDirectory = (Get-Location).Path.
Shayan Toqraee

79

현재 올바른 방법은 @Roman Kuzmin이 권장하는 솔루션을 @M 에 대한 의견으로 사용 하는 것입니다. 더들리 답변 :

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

(또한 불필요한 System네임 스페이스 설명 을 제거하여 약간 줄 였습니다. 기본적으로 자동으로 대체됩니다.)


2
이것은 (어떤 이유로 든) 나를 위해 BOM을 제거하지 못했습니다
Liam

@Liam, 이전 버전의 PowerShell 또는 .NET입니까?
ForNeVeR

1
이전 버전의 .NET WriteAllLines 함수는 기본적으로 BOM을 작성했다고 생각합니다. 따라서 버전 문제 일 수 있습니다.
벤더

2
Powershell 3에서는 BOM을 사용하지만 Powershell 4에서는 BOM을 사용하지 않는 쓰기로 확인되었습니다. M. Dudley의 원래 답변을 사용해야했습니다.
chazbot7

2
따라서 기본적으로 설치되는 Windows 10에서 작동합니다. :) 또한, 개선 제안 :[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
Johny Skovdal

50

나는 이것이 UTF-8이 아니라고 생각했지만 작동하는 것처럼 보이는 매우 간단한 해결책을 찾았습니다 ...

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

나에게 이것은 소스 형식에 관계없이 bom 파일이없는 utf-8이됩니다.


8
-encoding utf8내 요구 사항에 사용한 것을 제외하고는 나를 위해 일했습니다 .
Chim Chimz

1
대단히 감사합니다. 내부에 탭이있는 도구의 덤프 로그로 작업하고 있습니다. UTF-8이 작동하지 않았습니다. ASCII가 문제를 해결했습니다. 감사.
user1529294

44
예, -Encoding ASCIIBOM 문제를 피할 수 있지만 분명히 7 비트 ASCII 문자 만 얻을 수 있습니다. ASCII가 UTF-8의 서브 세트 인 경우 결과 파일은 기술적으로 유효한 UTF-8 파일이지만 입력의 모든 비 ASCII 문자는 리터럴 ?문자 로 변환됩니다 .
mklement0

4
@ChimChimz 실수로 귀하의 의견을 표명했지만 -encoding utf8BOM으로 UTF-8을 출력합니다. :(
TheDudeAbides

33

참고 :이 답변 Windows PowerShell에 적용됩니다 . 반대로 크로스 플랫폼 PowerShell Core 버전 (v6 +)에서 BOM없는 UTF-8 은 모든 cmdlet 에서 기본 인코딩 입니다.
즉 : 당신이 사용하는 경우 PowerShell을 [코어] 버전 6 이상을 , 당신의 get BOM없는 UTF-8 파일을 기본적으로 (당신이 명시 적으로 요청할 수 있습니다 -Encoding utf8/ -Encoding utf8NoBOM당신이 얻을 수있는 반면 와 -BOM 인코딩 -utf8BOM).


M. Dudley의 간단하고 실용적인 답변 을 보완하기 위해 (그리고 ForNeVeR의 더 간결한 개혁 ) :

편의를 위해, 여기에 고급 기능의 Out-FileUtf8NoBom, 모방하는 파이프 라인 기반의 대체Out-File 수단 :

  • 당신은 그것을 그대로 사용할 수 있습니다 Out-File파이프 라인 .
  • 문자열이 아닌 입력 개체는을 사용하여 콘솔에 보냈을 때와 같이 형식이 지정됩니다 Out-File.

예:

(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath

로 묶는 방법 (Get-Content $MyPath)에 유의하십시오 (...). 그러면 파이프 라인을 통해 결과를 보내기 전에 전체 파일을 열고, 전체를 읽고, 닫을 수 있습니다. 이것은 동일한 파일에 다시 쓸 수 있기 위해 필요 합니다 ( 제자리에서 업데이트 ).
그러나 일반적으로이 기술은 두 가지 이유로 권장되지 않습니다. (a) 전체 파일이 메모리에 맞아야하고 (b) 명령이 중단되면 데이터가 손실됩니다.

메모리 사용 에 대한 참고 사항 :

  • M. Dudley의 답변 에 따르면 전체 파일 내용을 메모리에 먼저 구축해야하므로 큰 파일에는 문제가 될 수 있습니다.
  • 아래 함수는이 기능을 약간만 개선합니다. 모든 입력 객체는 여전히 먼저 버퍼링되지만 문자열 표현이 생성되어 출력 파일에 하나씩 기록됩니다.

소스 코드Out-FileUtf8NoBom ( MIT 라이센스 Gist 로도 사용 가능 ) :

<#
.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()
  }

}

16

버전 6 부터 powershell은 set-contentout-fileUTF8NoBOM 모두에 대한 인코딩을 지원 하며 기본 인코딩으로도 사용합니다.

위의 예에서 간단히 다음과 같아야합니다.

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath

@ RaúlSalinas-Monteagudo 어떤 버전입니까?
John Bentley

좋은. 참고로$PSVersionTable.PSVersion
KCD

14

사용하는 경우 Set-Content대신 Out-File, 당신은 인코딩을 지정할 수 있습니다 Byte파일에 바이트 배열을 작성하는 데 사용할 수 있습니다. BOM을 내 보내지 않는 사용자 정의 UTF8 인코딩과 함께 사용하면 원하는 결과를 얻을 수 있습니다.

# 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

사용 [IO.File]::WriteAllLines()또는 유사 의 차이점 은 실제 파일 경로뿐만 아니라 모든 유형의 항목 및 경로에서 잘 작동한다는 것입니다.


5

이 스크립트는 BOM없이 UTF-8로 DIRECTORY1의 모든 .txt 파일을 변환하여 DIRECTORY2로 출력합니다.

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

이것은 경고없이 실패합니다. 실행하려면 어떤 버전의 powershell을 사용해야합니까?
darksoulsong

3
WriteAllLines 솔루션은 작은 파일에 적합합니다. 그러나 더 큰 파일에 대한 솔루션이 필요합니다. 더 큰 파일로 이것을 사용하려고 할 때마다 OutOfMemory 오류가 발생합니다.
BermudaLamb

2
    [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" 
    }  

소스 PowerShell을 사용하여 파일에서 UTF8 BOM (Byte Order Mark)을 제거하는 방법


2

당신이 사용하려는 경우 [System.IO.File]::WriteAllLines(), 당신은에 두 번째 매개 변수를 캐스팅해야 String[](의 유형이있는 경우 $MyFile이다 Object[]), 또한으로 절대 경로를 지정$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath) 같은 :

$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)

을 사용하려면 [System.IO.File]::WriteAllText()때로는 두 번째 매개 변수를 파이프하여 | Out-String |각 줄의 끝에 CRLF를 명시 적으로 추가해야합니다 (특히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)

또는 [Text.Encoding]::UTF8.GetBytes()함께 사용할 수 있습니다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"

BOM없이 UTF-8로 파일에 ConvertTo-Csv 결과를 쓰는 방법을 참조하십시오.


좋은 포인터; 제안 / :에 대한 간단한 대안 $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)Convert-Path $MyPath; 당신은 뒤에 CRLF를 확인하려는 경우, 단순히 사용 [System.IO.File]::WriteAllLines()로도 하나의 입력 문자열 (필요 없음 Out-String).
mklement0

0

내가 사용하는 한 가지 기술은 Out-File을 사용하여 출력을 ASCII 파일로 리디렉션하는 것입니다 cmdlet을 입니다.

예를 들어, Oracle에서 실행할 다른 SQL 스크립트를 작성하는 SQL 스크립트를 자주 실행합니다. 간단한 재 지정 ( ">")을 사용하면 출력이 UTF-16으로되어 SQLPlus에서 인식되지 않습니다. 이 문제를 해결하려면

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

그런 다음 생성 된 스크립트는 유니 코드 걱정없이 다른 SQLPlus 세션을 통해 실행될 수 있습니다.

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

4
예, -Encoding ASCIIBOM 문제를 피할 수 있지만 분명히 7 비트 ASCII 문자 만 지원 합니다 . ASCII가 UTF-8의 서브 세트 인 경우 결과 파일은 기술적으로 유효한 UTF-8 파일이지만 입력의 모든 비 ASCII 문자는 리터럴 ?문자 로 변환됩니다 .
mklement0

이 답변에는 더 많은 투표가 필요합니다. BOM과의 sqlplus 비 호환성은 많은 두통 의 원인입니다 .
Amit Naidu

0

BOM없이 확장자로 여러 파일을 UTF-8로 변경하십시오.

$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)
}

0

어떤 이유에서든, WriteAllLines콜리스는 BOMless UTF8Encoding인수와 함께없이 BOM을 계속 생성하고 있었습니다. 그러나 다음은 저에게 효과적이었습니다.

$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])

파일 경로가 절대적으로 작동하도록해야했습니다. 그렇지 않으면 파일을 내 데스크탑에 썼습니다. 또한 BOM이 3 바이트임을 알고있는 경우에만 작동한다고 가정합니다. 인코딩을 기반으로 주어진 BOM 형식 / 길이를 기대하는 것이 얼마나 안정적인지 잘 모르겠습니다.

또한 작성된 것처럼 파일이 powershell 배열에 맞는 경우에만 작동하며 길이 제한이 [int32]::MaxValue내 컴퓨터 보다 낮습니다 .


1
WriteAllLines인코딩 인수가 없으면 BOM 자체를 쓰지 않지만 문자열 이 BOM 문자 ( U+FEFF) 로 시작 하여 UTF-8 BOM을 효과적으로 생성 한 것으로 생각할 수 있습니다 . 예 : $s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)( BOM이 작성 되지 않았 음[char] 0xfeff + 을 보려면 생략 ).
mklement0

1
예기치 않게 다른 위치에 쓰는 경우 문제는 .NET 프레임 워크가 일반적으로 PowerShell과 다른 현재 디렉토리를 가지고 있다는 것입니다. 먼저 동기화 할 수 있습니다[Environment]::CurrentDirectory = $PWD.ProviderPath 하거나 "$(pwd)\..."접근 방식에 대한보다 일반적인 대안 (더 "$pwd\..."나은 : "$($pwd.ProviderPath)\..."또는 더 나은 : 또는 (Join-Path $pwd.ProviderPath ...))으로(Convert-Path BOMthetorpedoes.txt)
mklement0

고마워, 나는 단일 BOM 문자를 UTF-8 BOM으로 변환 할 수 있다는 것을 몰랐다.
xdhmoore

1
모든 BOM 바이트 시퀀스 (유니 코드 서명)는 사실 초록의 해당 인코딩의 바이트 표현입니다. 단일 유니 코드 문자U+FEFF .
mklement0

그래. 그것은 일을 더 단순하게 만드는 것처럼 보입니다.
xdhmoore

-2

BOM없이 UTF8을 얻기 위해 아래를 사용할 수 있습니다.

$MyFile | Out-File -Encoding ASCII

4
아니요, 출력을 현재 ANSI 코드 페이지 (예 : cp1251 또는 cp1252)로 변환합니다. 전혀 UTF-8이 아닙니다!
ForNeVeR

1
고마워 로빈 BOM없이 UTF-8 파일을 작성하는 데에는 효과가 없었지만 -Encoding ASCII 옵션은 BOM을 제거했습니다. 그렇게하면 gvim에 대한 박쥐 파일을 생성 할 수 있습니다. .bat 파일이 BOM에서 트립되었습니다.
Greg Greg

3
@ForNeVeR : 인코딩 ASCII이 UTF-8이 아니라는 것이 맞지만 현재 ANSI 코드 페이지가 아닙니다 Default. ASCII실제로 7 비트 ASCII 인코딩이며 128보다 큰 코드 포인트는 리터럴 ?인스턴스 로 변환됩니다 .
mklement0

1
@ForNeVeR : 아마도 "ANSI"또는 " 확장 ASCII"를 생각하고있을 것입니다 . -Encoding ASCII실제로 7 비트 ASCII 전용인지 확인하려면 다음을 시도하십시오 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f).- ä이 (가) 음역되었습니다 ?. 반대로 -Encoding Default( "ANSI")는이를 올바르게 보존합니다.
mklement0

3
@rob 이것은 UTF-8이나 ASCII와 다른 것이 필요하지 않고 인코딩과 유니 코드의 목적에 관심이없는 모든 사람들에게 완벽한 해답입니다. 모든 ASCII 문자와 동등한 utf-8 문자가 동일하기 때문에 utf-8로 사용할 수 있습니다 (ASCII 파일을 utf-8 파일로 변환하면 동일한 파일이 생성됨 (BOM이없는 경우)). 텍스트에 비 ASCII 문자가있는 모든 사람에게이 대답은 허위이며 오해의 소지가 있습니다.
TNT

-3

이것은 나를 위해 작동합니다 ( "UTF8"대신 "Default"사용).

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

결과는 BOM이없는 ASCII입니다.


1
아웃 파일 문서 시방 Default내가 필요에 따라, UTF-8하지 않은 시스템의 현재 ANSI 코드 페이지를 사용 인코딩.
M. Dudley

이것은 적어도 Export-CSV에서 나에게 효과가있는 것 같습니다. 결과 파일을 적절한 편집기에서 열면 파일 인코딩은 BOM이없는 UTF-8이며 ASCII가 예상 한 서부 라틴어 ISO 9가 아닙니다
eythort

인코딩을 감지 할 수없는 경우 많은 편집자가 파일을 UTF-8로 엽니 다.
emptyother
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.