특정 확장자를 가진 모든 파일을 찾고 바꾸기위한 PowerShell 스크립트


123

Windows Server 2008에 다음과 같이 중첩 된 여러 구성 파일이 있습니다.

C:\Projects\Project_1\project1.config

C:\Projects\Project_2\project2.config

내 구성에서 다음과 같은 문자열 교체를 수행해야합니다.

<add key="Environment" value="Dev"/>

될 것입니다:

<add key="Environment" value="Demo"/>

일괄 스크립팅을 사용하는 것에 대해 생각했지만이 작업을 수행하는 좋은 방법이 없었고 PowerShell 스크립팅을 사용하면 쉽게 수행 할 수 있다고 들었습니다. 찾기 / 바꾸기의 예를 찾았지만 내 C : \ Projects 디렉터리 내의 모든 폴더를 탐색하고 '.config'확장자로 끝나는 파일을 찾는 방법을 찾고있었습니다. 하나를 찾으면 내 문자열 값을 바꾸고 싶습니다.

이 작업을 수행하는 방법을 찾을 수있는 좋은 리소스 또는 통찰력을 제공 할 수있는 PowerShell 전문가가 있습니까?


1
어떻게했는지 또는 해결해야 할 파일에 이상한 형식 문제가 있는지 알려주십시오. 문제에 대한 한 가지 좋은 점은 프로덕션 코드에 영향을주지 않고 테스트한다는 것입니다.
Robben_Ford_Fan_boy

답변:


178

여기 내 머리 꼭대기에서 첫 번째 시도.

$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace "Dev", "Demo" } |
    Set-Content $file.PSPath
}

3
제공된 솔루션 테스트가 모두 효과가 있다는 것을 추가하고 싶지만 이것은 가독성을 위해 가장 쉬운 방법이었습니다. 나는 이것을 동료에게 전달할 수 있었고 그는 무슨 일이 일어나고 있는지 쉽게 이해할 수있었습니다. 도움을 주셔서 감사합니다.
Brandon

11
이 작업이 하위 디렉터리의 파일에서 작동하려면 ".PSPath"가 필요합니다. 흥미롭게도 get-content 주위에 ()없이이 작업을하려고했을 때 파일이 잠겨 있었기 때문에 write-content에서 실패했습니다.
Frank Schwieterman 2010 년

25
짧은 버전 (일반적인 별칭 사용) :ls *.config -rec | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Dev", "Demo" } | sc $f.PSPath }
Artyom

5
@Artyom은 잊지 마세요 .애프터 ls. 나 자신에 의해 쏘였습니다.
Pureferret 2015 년

5
UnauthorizedAccessException은 모든 파일에서 실행되도록 * .config를 제거하는 경우 폴더로 인해 발생할 수도 있습니다. -File 필터를 Get-ChildItem에 추가 할 수 있습니다 ... 알아내는 데 시간이 좀 걸렸습니다
Amir Katz

32

PowerShell은 좋은 선택입니다.;) 주어진 디렉토리에서 파일을 열거하고 읽고 처리하는 것은 매우 쉽습니다.

스크립트는 다음과 같습니다.

Get-ChildItem C:\Projects *.config -recurse |
    Foreach-Object {
        $c = ($_ | Get-Content) 
        $c = $c -replace '<add key="Environment" value="Dev"/>','<add key="Environment" value="Demo"/>'
        [IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
    }

나는 당신이 읽을 수 있도록 코드를 더 많은 줄로 나누었습니다. 대신 Set-Content를 사용할 수 [IO.File]::WriteAllText있지만 끝에 새 줄이 추가됩니다. WriteAllText당신 과 함께 그것을 피할 수 있습니다.

그렇지 않으면 코드는 다음과 같습니다 $c | Set-Content $_.FullName..


14

이 접근 방식은 잘 작동합니다.

gci C:\Projects *.config -recurse | ForEach {
  (Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_ 
}
  • "이전"및 "새"를 해당 값으로 변경하거나 변수를 사용합니다.
  • 괄호를 잊지 마십시오. 그렇지 않으면 액세스 오류가 발생합니다.

그래서 나는 간결한 표현이 하나 갔다 -하지만 난 대체했다 Get-Content $_Get-Content $_.FullName하고, 대한 등가 Set-Content는 루트되지 않은 파일을 처리 할 수.
매트 윗 필드

11

xml과 xpath를 사용합니다.

dir C:\Projects\project_*\project*.config -recurse | foreach-object{  
   $wc = [xml](Get-Content $_.fullname)
   $wc.SelectNodes("//add[@key='Environment'][@value='Dev']") | Foreach-Object {$_.value = 'Demo'}  
   $wc.Save($_.fullname)  
}

11

이 powershell 예제는 폴더 및 하위 폴더에서 "\ foo \"문자열의 모든 인스턴스를 찾고 "\ foo \"를 "\ bar \"로 바꾸며 문자열 "\ foo \를 포함하지 않는 파일을 다시 쓰지 않습니다. "이렇게하면 문자열이 발견되지 않은 마지막 업데이트 날짜 / 시간 스탬프 파일을 파괴하지 않습니다.

Get-ChildItem  -Path C:\YOUR_ROOT_PATH\*.* -recurse 
 | ForEach {If (Get-Content $_.FullName | Select-String -Pattern '\\foo\\') 
           {(Get-Content $_ | ForEach {$_ -replace '\\foo\\', '\bar\'}) | Set-Content $_ }
           }

7

@Artyom의 의견이 유용하다는 것을 알았지 만 불행히도 그는 답변을 게시하지 않았습니다.

이것은 내 생각에 가장 좋은 버전의 짧은 버전입니다.

ls *.config -rec | %{$f=$_; (gc $f.PSPath) | %{$_ -replace "Dev", "Demo"} | sc $f.PSPath}

1
내가 한 것처럼 다른 사람이 이것을 실행하는 경우-배치 파일에서 직접 실행하려고- 이와 같은 명령을 실행할 때 별칭 foreach-object대신 사용하는 것이 도움이 될 수 있습니다 %. 그렇지 않으면, 오류가 발생할 수 있습니다 :Expressions are only allowed as the first element of a pipeline
더스틴 홀스 테드

짧은 것이 항상 좋은 것은 아니며 긴 버전에서 일어나는 일이 더 분명합니다.
Nick N.

@NickN. 글쎄요. 그것은 당신의 목표가 무엇인지에 달려 있습니다. 나는 그것을 POB로 표시 할 것이다;)
M--

6

파일의 텍스트를 대체하는 작은 도우미 함수를 작성했습니다.

function Replace-TextInFile
{
    Param(
        [string]$FilePath,
        [string]$Pattern,
        [string]$Replacement
    )

    [System.IO.File]::WriteAllText(
        $FilePath,
        ([System.IO.File]::ReadAllText($FilePath) -replace $Pattern, $Replacement)
    )
}

예:

Get-ChildItem . *.config -rec | ForEach-Object { 
    Replace-TextInFile -FilePath $_ -Pattern 'old' -Replacement 'new' 
}

4

재귀 적 교체를 수행 할 때 경로와 파일 이름을 포함해야합니다.

Get-ChildItem -Recurse | ForEach {  (Get-Content $_.PSPath | 
ForEach {$ -creplace "old", "new"}) | Set-Content $_.PSPath }

이렇게하면 현재 디렉토리 폴더의 모든 파일에서 모든 "이전"이 대소 문자를 구분하는 "새"로 바뀝니다.


답변의 ".PSPath"부분이 정말 도움이되었습니다. 하지만 내부 "{$"를 "$ _"로 변경해야했습니다. 나는 당신의 대답을 편집 싶지만, 난 당신의 -creplace 부분을 사용하지 않는 - 미안이 .PSPath으로 허용 대답을 사용하여
AAAA를 BBBB
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.