Tập lệnh PowerShell để tìm và thay thế cho tất cả các tệp có phần mở rộng cụ thể


123

Tôi có một số tệp cấu hình trên Windows Server 2008 được lồng vào nhau như vậy:

C:\Projects\Project_1\project1.config

C:\Projects\Project_2\project2.config

Trong cấu hình của mình, tôi cần thực hiện một chuỗi thay thế như sau:

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

sẽ trở thành:

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

Tôi đã nghĩ đến việc sử dụng tập lệnh hàng loạt, nhưng không có cách nào tốt để thực hiện việc này và tôi nghe nói rằng với tập lệnh PowerShell, bạn có thể dễ dàng thực hiện điều này. Tôi đã tìm thấy các ví dụ về tìm / thay thế, nhưng tôi đã hy vọng một cách có thể duyệt qua tất cả các thư mục trong thư mục C: \ Projects của tôi và tìm thấy bất kỳ tệp nào kết thúc bằng phần mở rộng '.config'. Khi nó tìm thấy một, tôi muốn nó thay thế các giá trị chuỗi của tôi.

Bất kỳ tài nguyên tốt nào để tìm hiểu cách thực hiện việc này hoặc bất kỳ chuyên gia PowerShell nào có thể cung cấp một số thông tin chi tiết?


1
Hãy cho chúng tôi biết bạn đã tiếp tục như thế nào hoặc nếu có một số vấn đề định dạng kỳ lạ với các tệp cần được giải quyết. Một điều tốt về vấn đề là nó đang thử nghiệm mà không ảnh hưởng đến mã sản xuất
Robben_Ford_Fan_boy

Câu trả lời:


178

Đây là một nỗ lực đầu tiên trên đỉnh đầu của tôi.

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

3
Tôi muốn nói thêm rằng thử nghiệm các giải pháp được cung cấp đều hoạt động, nhưng giải pháp này là dễ đọc nhất. Tôi đã có thể giao nó cho một đồng nghiệp và anh ấy có thể dễ dàng hiểu chuyện gì đang xảy ra. Cảm ơn vì sự hỗ trợ.
Brandon

11
Để điều này hoạt động trong các tệp trong thư mục con, bạn cần ".PSPath". Thật thú vị, khi tôi cố gắng làm cho nó hoạt động mà không có () xung quanh get-content, nó không thành công khi ghi nội dung vì tệp đã bị khóa.
Frank Schwieterman

25
Phiên bản ngắn (bí danh phổ biến được sử dụng):ls *.config -rec | %{ $f=$_; (gc $f.PSPath) | %{ $_ -replace "Dev", "Demo" } | sc $f.PSPath }
Artyom

5
@Artyom đừng quên .sau ls. Chính tôi cũng bị đốt bởi điều đó.
Pureferret

5
UnauthorizedAccessException cũng có thể do thư mục nếu bạn xóa * .config để chạy trên tất cả các tệp. Bạn có thể thêm bộ lọc -File vào Get-ChildItem ... Phải mất một lúc để tìm ra nó
Amir Katz

32

PowerShell là một lựa chọn tốt;) Rất dễ dàng liệt kê các tệp trong thư mục nhất định, đọc chúng và xử lý.

Tập lệnh có thể trông như thế này:

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

Tôi chia mã thành nhiều dòng hơn để bạn có thể đọc được. Lưu ý rằng bạn có thể sử dụng Set-Content thay thế [IO.File]::WriteAllText, nhưng nó sẽ thêm dòng mới ở cuối. VớiWriteAllText bạn có thể tránh nó.

Nếu mã có thể trông như thế này: $c | Set-Content $_.FullName.


14

Cách tiếp cận này hoạt động tốt:

gci C:\Projects *.config -recurse | ForEach {
  (Get-Content $_ | ForEach {$_ -replace "old", "new"}) | Set-Content $_ 
}
  • Thay đổi "cũ" và "mới" thành các giá trị tương ứng của chúng (hoặc sử dụng các biến).
  • Đừng quên dấu ngoặc đơn - nếu không có dấu ngoặc đơn, bạn sẽ nhận được lỗi truy cập.

Vì vậy, tôi đã sử dụng biểu thức này cho biểu thức ngắn gọn - nhưng tôi phải thay thế Get-Content $_bằng Get-Content $_.FullNamevà tương đương Set-Contentđể nó xử lý các tệp không có ở gốc.
Matt Whitfield

11

Tôi sẽ sử dụng xml và 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

Ví dụ về powershell này tìm kiếm tất cả các trường hợp của chuỗi "\ foo \" trong một thư mục và các thư mục con của nó, thay thế "\ foo \" bằng "\ bar \" VÀ KHÔNG VIẾT các tệp không chứa chuỗi "\ foo \ "Bằng cách này, bạn không phá hủy tệp ngày giờ cập nhật cuối cùng đóng dấu nơi không tìm thấy chuỗi:

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

Tôi thấy bình luận của @Artyom hữu ích nhưng tiếc là anh ấy chưa đăng câu trả lời.

Đây là phiên bản ngắn, theo quan điểm của tôi, phiên bản tốt nhất của câu trả lời được chấp nhận;

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

1
Trong trường hợp bất kỳ ai khác gặp phải điều này, như tôi đã làm - tìm cách thực thi điều này trực tiếp từ một tệp lô - Có thể hữu ích khi sử dụng foreach-objectthay cho %bí danh khi thực hiện một lệnh như thế này. Nếu không, nó có thể dẫn đến lỗi:Expressions are only allowed as the first element of a pipeline
Dustin Halstead

Ngắn không phải lúc nào cũng tốt hơn, rõ ràng hơn những gì đang xảy ra trong phiên bản dài.
Nick N.

@NickN. Vâng, đúng. Nó phụ thuộc vào mục tiêu của bạn là gì. Tôi sẽ gắn cờ nó là POB;)
M--

6

Tôi đã viết một hàm trợ giúp nhỏ để thay thế văn bản trong tệp:

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

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

Thí dụ:

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

4

Khi thực hiện thay thế đệ quy, đường dẫn và tên tệp cần được bao gồm:

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

Wil này sẽ thay thế tất cả "cũ" bằng "mới" phân biệt chữ hoa chữ thường trong tất cả các tệp trong thư mục của thư mục hiện tại của bạn.


Phần ".PSPath" trong câu trả lời của bạn thực sự giúp ích cho tôi. Nhưng tôi đã phải thay đổi "{$" bên trong thành "$ _". Tôi muốn chỉnh sửa câu trả lời của bạn, nhưng tôi không sử dụng phần -creplace của bạn - Tôi đang sử dụng câu trả lời được chấp nhận với .PSPath
aaaa bbbb
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.