파일에서 모든 문자열을 PowerShell로 바꾸려면 어떻게해야합니까?


289

PowerShell을 사용 [MYID]하여 주어진 파일에서 발생하는 모든 정확한 항목을로 바꾸고 싶습니다 MyValue. 가장 쉬운 방법은 무엇입니까?


이 질문에 대한 답변에서 제공하는 것보다 메모리 소비 측면에서 더 효과적인 솔루션을 보려면 큰 파일에서 찾기 및 바꾸기를 참조하십시오 .
마틴 Prikryl

답변:


444

사용 (V3 버전) :

(Get-Content c:\temp\test.txt).replace('[MYID]', 'MyValue') | Set-Content c:\temp\test.txt

또는 V2의 경우 :

(Get-Content c:\temp\test.txt) -replace '\[MYID\]', 'MyValue' | Set-Content c:\temp\test.txt

3
감사합니다. "바꾸기 : [System.Object []]에 'replace'라는 이름의 메소드가 없기 때문에 메소드 호출에 실패했습니다."라는 오류가 발생합니다. 그러나?
아마추어

내가 방금 발견 한 PS v4에서 이스케이프가 작동합니다. 감사.
ErikE

4
@rob 수정 사항을 저장하려면 결과를 set-content 또는 out-file로 파이프하십시오.
Loïc MICHEL

2
"[System.Object []]에 'replace'라는 이름의 메소드가 없기 때문에 메소드 호출에 실패했습니다." 라는 오류가 발생 했습니다. V2 만있는 머신에서 V3 버전을 실행하려고했기 때문입니다.
SFlagg

5
경고 : 큰 파일 (수백 메가 바이트 정도)에 대해이 스크립트를 실행하면 상당한 양의 메모리가 소모 될 수 있습니다. 프로덕션 서버에서 실행중인 경우 충분한 헤드 룸이 있는지 확인하십시오. D
neoscribe

89
(Get-Content file.txt) | 
Foreach-Object {$_ -replace '\[MYID\]','MyValue'}  | 
Out-File file.txt

괄호 (Get-Content file.txt)는 다음과 같습니다.

괄호가 없으면 내용이 한 번에 한 줄씩 읽혀지고 파일 또는 세트 내용에 도달 할 때까지 파이프 라인을 따라 내려갑니다. 동일한 파일에 쓰려고 시도하지만 get-content에 의해 이미 열려 있고 오류. 괄호로 인해 콘텐츠 읽기 작업이 한 번만 수행됩니다 (열기, 읽기 및 닫기). 그런 다음에 만 모든 행을 읽을 때 한 번에 하나씩 파이프되고 파이프 라인의 마지막 명령에 도달하면 파일에 쓸 수 있습니다. $ content = content와 동일합니다. $ content | 어디 ...


5
가능하면 공감 비를 공감 비로 바꿨습니다. PowerShell 3에서는 파일의 모든 내용을 자동으로 삭제합니다! yuou Set-Content대신 사용하면 "다른 프로세스에서 '123.csv'파일을 사용 중이기 때문에 프로세스가 파일에 액세스 할 수 없습니다."Out-File 와 같은 경고가 표시 됩니다. .
Iain Samuel McLean 장로

9
get-content가 괄호 안에 있으면 발생하지 않아야합니다. 작업으로 인해 파일을 열고, 읽고, 닫아서 오류가 발생하지 않아야합니다. 샘플 텍스트 파일로 다시 테스트 할 수 있습니까?
Shay Levy

2
함께 Get-Content괄호 안에 그것을 작동합니다. 괄호가 필요한 이유를 답변에 설명해 주시겠습니까? 나는 아직도 대체 할 Out-FileSet-Content는 더 안전하기 때문에; 괄호를 잊어 버린 경우 대상 파일을 지우는 것을 방지합니다.
Iain Samuel McLean Elder

6
파일 인코딩 UTF-8에 문제가 있습니다 . 파일을 저장하면 인코딩이 변경됩니다. 동일하지 않습니다. stackoverflow.com/questions/5596982/… . set-content 는 인코딩 파일 (UTF-8과 같은)을 고려 한다고 생각 합니다 . 하지만 파일이
아님

1
이 솔루션은 불필요하게 오도되어 사용했을 때 문제를 일으켰습니다. 설치 프로세스에서 즉시 사용했던 구성 파일을 업데이트하고있었습니다. 구성 파일이 여전히 프로세스에 의해 보류되었으며 설치에 실패했습니다. Set-Content대신에 사용 하는 Out-File것이 훨씬 더 좋고 안전한 솔루션입니다. 공감해야합니다.
Martin Basista

81

다음 예제에서 볼 수 있듯이 File 클래스의 .NET과 정적 메서드를 사용하는 것이 좋습니다.

$content = [System.IO.File]::ReadAllText("c:\bla.txt").Replace("[MYID]","MyValue")
[System.IO.File]::WriteAllText("c:\bla.txt", $content)

이것은 Get-Content 와 같이 String-array 대신 단일 String으로 작업 할 수 있다는 이점이 있습니다 . 또한 대부분의 시간을 신경 쓸 필요없이 파일 인코딩 (UTF-8 BOM 등)을 처리합니다.

또한 메서드는 Get-Content를 사용하고 Set-Content로 파이프하는 알고리즘과 달리 줄 끝 (사용할 수있는 Unix 줄 끝)을 엉망으로 만들지 않습니다. .

그래서 나를 위해 : 몇 년 동안 깰 수있는 것들이 적습니다.

.NET 클래스를 사용할 때 잘 알려진 점은 PowerShell 창에서 "[System.IO.File] ::"을 입력하면 Tab키를 눌러 방법을 단계별로 수행 할 수 있다는 것입니다.


다음 명령으로 메소드를 볼 수도 있습니다. [System.IO.File] | gm
fbehrens

이 방법은 왜 상대 경로를 가정 C:\Windows\System32\WindowsPowerShell\v1.0합니까?
Adrian

그렇습니까? 아마도 PowerShell 내에서 .NET AppDomain이 시작되는 방식과 관련이있을 수 있습니다. cd를 사용할 때 현재 경로가 업데이트되지 않을 수 있습니다. 그러나 이것은 교육받은 추측에 지나지 않습니다. 나는 이것을 테스트하거나 찾지 않았다.
rominator007

2
다른 버전의 Powershell에 대해 다른 코드를 작성하는 것보다 훨씬 쉽습니다.
Willem van Ketwich

이 방법도 가장 빠른 것으로 보입니다. 언급 된 이점과 의문과 함께 "왜 다른 것을 사용하고 싶습니까?"
DBADon

21

위의 파일은 "하나의 파일"에 대해서만 실행되지만 폴더 내의 여러 파일에 대해서도 실행할 수 있습니다.

Get-ChildItem 'C:yourfile*.xml' -Recurse | ForEach {
     (Get-Content $_ | ForEach  { $_ -replace '[MYID]', 'MyValue' }) |
     Set-Content $_
}

.xml을 사용했지만 .txt로 바꿀 수 있습니다.
John V Hobbs Jr

좋은. 내부를 사용하는 대신 foreach이것을 할 수 있습니다Get-ChildItem 'C:\folder\file*.xml' -Recurse | ForEach { (Get-Content $_).Replace('[MYID]', 'MyValue') | Set-Content $_ }
KCD

1
실제로 foreachGet-Content가 예상치 못한 작업을 수행 하기 때문에 inner가 필요합니다 . 각 문자열이 파일의 한 줄인 문자열 배열을 반환합니다. 실행중인 스크립트와 다른 위치에있는 디렉토리 (및 하위 디렉토리)를 반복하는 경우 다음과 같은 것을 원할 것입니다 : 수정하려는 파일이 들어있는 디렉토리는 Get-ChildItem $Directory -File -Recurse | ForEach { (Get-Content $_.FullName) | ForEach { $_ -replace '[MYID]', 'MyValue' } | Set-Content $_.FullName }어디에 있습니까 $Directory?
birdamongmen

1
"위의 답변"은 무엇입니까?
피터 Mortensen

10

다음과 같이 시도해보십시오.

$path = "C:\testFile.txt"
$word = "searchword"
$replacement = "ReplacementText"
$text = get-content $path 
$newText = $text -replace $word,$replacement
$newText > $path

7

이것이 내가 사용하는 것이지만 큰 텍스트 파일에서는 느립니다.

get-content $pathToFile | % { $_ -replace $stringToReplace, $replaceWith } | set-content $pathToFile

큰 텍스트 파일에서 문자열을 바꾸고 속도가 중요한 경우 System.IO.StreamReaderSystem.IO.StreamWriter 사용을 살펴보십시오 .

try
{
   $reader = [System.IO.StreamReader] $pathToFile
   $data = $reader.ReadToEnd()
   $reader.close()
}
finally
{
   if ($reader -ne $null)
   {
       $reader.dispose()
   }
}

$data = $data -replace $stringToReplace, $replaceWith

try
{
   $writer = [System.IO.StreamWriter] $pathToFile
   $writer.write($data)
   $writer.close()
}
finally
{
   if ($writer -ne $null)
   {
       $writer.dispose()
   }
}

(위의 코드는 테스트되지 않았습니다.)

문서에서 텍스트를 바꾸는 데 StreamReader와 StreamWriter를 사용하는 더 우아한 방법이있을 수 있지만 좋은 출발점이 될 것입니다.


set-content는 인코딩 파일 (UTF-8과 같은)을 고려한다고 생각합니다. 하지만 아웃 파일이 아닙니다 stackoverflow.com/questions/5596982/…
Kiquenet

2

Payette의 Windows Powershell in Action 에서 조금 알려졌지만 놀랍도록 멋진 방법을 찾았습니다 . $ env : path와 비슷한 변수와 같은 파일을 참조 할 수 있지만 중괄호를 추가해야합니다.

${c:file.txt} = ${c:file.txt} -replace 'oldvalue','newvalue'

파일 이름이 다음과 같은 변수에 있으면 어떻게 $myFile됩니까?
ΩmegaMan

@ ΩmegaMan 흠 지금까지만$a = 'file.txt'; invoke-expression "`${c:$a} = `${c:$a} -replace 'oldvalue','newvalue'"
js2010

2

여러 파일에서 문자열을 교체해야하는 경우 :

여기에 게시 된 다른 방법은 완료하는 데 걸리는 시간과 크게 다를 수 있습니다. 나를 위해 정기적으로 많은 수의 작은 파일이 있습니다. 가장 성능이 좋은 것을 테스트하기 위해 40,693 개의 별도 파일에서 5.52GB (5,933,604,999 바이트)의 XML을 추출하고 여기에서 찾은 세 가지 답변을 실행했습니다.

## 5.52 GB (5,933,604,999 bytes) of XML files (40,693 files) 

#### Test 1 - Plain Replace
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml).replace("'", " ") | Set-Content $xml
}
$end   = get-date
NEW-TIMESPAN Start $Start End $End
<#
TotalMinutes: 103.725113128333
#>

#### Test 2 - Replace with -Raw
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
(Get-Content $xml -Raw).replace("'", " ") | Set-Content $xml
}
$end   = get-date
NEW-TIMESPAN Start $Start End $End
<#
TotalMinutes: 10.1600227983333
#>

#### Test 3 - .NET, System.IO
$start = get-date
$xmls = (Get-ChildItem -Path "I:\TestseT\All_XML" -Recurse -Filter *.xml).FullName
foreach ($xml in $xmls)
{
$txt = [System.IO.File]::ReadAllText("$xml").Replace("'"," ") 
[System.IO.File]::WriteAllText("$xml", $txt)
}
$end   = get-date
NEW-TIMESPAN Start $Start End $End
<#
TotalMinutes: 5.83619516833333
#>

문제는 여러 파일이 아닌 주어진 파일에서 문자열을 바꾸는 것입니다.
PL

1

@ rominator007에 신용

함수에 래핑했습니다 (다시 사용하고 싶을 수도 있기 때문에)

function Replace-AllStringsInFile($SearchString,$ReplaceString,$FullPathToFile)
{
    $content = [System.IO.File]::ReadAllText("$FullPathToFile").Replace("$SearchString","$ReplaceString")
    [System.IO.File]::WriteAllText("$FullPathToFile", $content)
}

참고 : 이것은 대소 문자를 구분하지 않습니다 !!!!!

이 게시물을 참조하십시오 : String. 대소 문자를 무시 하십시오.


0

이것은 PowerShell에서 현재 작업 디렉토리를 사용하여 나를 위해 일했습니다. 이 FullName속성 을 사용해야합니다. 그렇지 않으면 PowerShell 버전 5에서 작동하지 않습니다 CSPROJ. 모든 파일 에서 대상 .NET 프레임 워크 버전을 변경해야했습니다 .

gci -Recurse -Filter *.csproj |
% { (get-content "$($_.FullName)")
.Replace('<TargetFramework>net47</TargetFramework>', '<TargetFramework>net462</TargetFramework>') |
 Set-Content "$($_.FullName)"}

0

특정 파일 이름의 모든 인스턴스에서 특정 줄을 변경해야했기 때문에 조금 오래되었습니다.

또한 Set-Content일관된 결과를 반환하지 않았으므로에 의지해야했습니다 Out-File.

아래 코드 :


$FileName =''
$OldLine = ''
$NewLine = ''
$Drives = Get-PSDrive -PSProvider FileSystem
foreach ($Drive in $Drives) {
    Push-Location $Drive.Root
        Get-ChildItem -Filter "$FileName" -Recurse | ForEach { 
            (Get-Content $_.FullName).Replace($OldLine, $NewLine) | Out-File $_.FullName
        }
    Pop-Location
}

이것이이 PowerShell 버전에서 가장 효과적이었습니다.

주요 사소한 건물 개정

5.1.16299.98


-1

Set-Content 명령에 대한 작은 수정. 검색된 문자열을 찾지 못하면 Set-Content명령은 대상 파일을 비 웁니다 (비어 있음).

찾고있는 문자열이 존재하는지 먼저 확인할 수 있습니다. 그렇지 않으면 아무것도 교체되지 않습니다.

If (select-string -path "c:\Windows\System32\drivers\etc\hosts" -pattern "String to look for") `
    {(Get-Content c:\Windows\System32\drivers\etc\hosts).replace('String to look for', 'String to replace with') | Set-Content c:\Windows\System32\drivers\etc\hosts}
    Else{"Nothing happened"}

3
StackOverflow에 오신 것을 환영합니다! 서식을 사용 하십시오 . 도움이 필요한 경우이 기사를 읽을 수 있습니다 .
CodenameLambda

1
정답을 사용하고 교체를 찾을 수 없으면 여전히 파일을 쓰지만 변경 사항은 없습니다. 예 set-content test.txt "hello hello world hello world hello"다음은 (get-content .\test.txt).Replace("something", "awesome") | set-content .\test.txt이에 제안 파일을 비워하지 않습니다.
Ciantic
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.