Cách thay thế nhiều chuỗi trong một tệp bằng PowerShell


106

Tôi đang viết một tập lệnh để tùy chỉnh tệp cấu hình. Tôi muốn thay thế nhiều trường hợp của chuỗi trong tệp này và tôi đã thử sử dụng PowerShell để thực hiện công việc.

Nó hoạt động tốt cho một lần thay thế, nhưng thực hiện nhiều lần thay thế thì rất chậm vì mỗi lần phải phân tích cú pháp lại toàn bộ tệp và tệp này rất lớn. Tập lệnh trông như thế này:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
    $_ -replace 'something1', 'something1new'
    } | Set-Content $destination_file

Tôi muốn một cái gì đó như thế này, nhưng tôi không biết làm thế nào để viết nó:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
    $_ -replace 'something1', 'something1aa'
    $_ -replace 'something2', 'something2bb'
    $_ -replace 'something3', 'something3cc'
    $_ -replace 'something4', 'something4dd'
    $_ -replace 'something5', 'something5dsf'
    $_ -replace 'something6', 'something6dfsfds'
    } | Set-Content $destination_file

Câu trả lời:


167

Một lựa chọn là chuỗi các -replacehoạt động lại với nhau. Ở `cuối mỗi dòng thoát khỏi dòng mới, khiến PowerShell tiếp tục phân tích cú pháp biểu thức trên dòng tiếp theo:

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'
(Get-Content $original_file) | Foreach-Object {
    $_ -replace 'something1', 'something1aa' `
       -replace 'something2', 'something2bb' `
       -replace 'something3', 'something3cc' `
       -replace 'something4', 'something4dd' `
       -replace 'something5', 'something5dsf' `
       -replace 'something6', 'something6dfsfds'
    } | Set-Content $destination_file

Một tùy chọn khác sẽ là gán một biến trung gian:

$x = $_ -replace 'something1', 'something1aa'
$x = $x -replace 'something2', 'something2bb'
...
$x

$ Original_file == $ đích_file được không? Như trong tôi đang sửa đổi cùng một tệp với nguồn của tôi?
cquadrini

Do cách các lệnh ghép ngắn PowerShell truyền trực tuyến đầu vào / đầu ra của chúng, tôi không tin rằng việc ghi ra cùng một tệp trong cùng một đường dẫn sẽ hoạt động. Tuy nhiên, bạn có thể làm điều gì đó như $c = Get-Content $original_file; $c | ... | Set-Content $original_file.
dahlbyk

Bạn có gặp sự cố về mã hóa tệp bằng Set-Content không chứa mã gốc không? Ví dụ: mã hóa UTF-8 hoặc ANSI.
Kiquenet

1
Yeah PowerShell là ... vô ích như vậy. Bạn phải tự phát hiện mã hóa, ví dụ: github.com/dahlbyk/posh-git/blob/…
dahlbyk

24

Để bài đăng của George Howarth hoạt động bình thường với nhiều lần thay thế, bạn cần xóa dấu ngắt, gán đầu ra cho một biến ($ line) và sau đó xuất biến:

$lookupTable = @{
    'something1' = 'something1aa'
    'something2' = 'something2bb'
    'something3' = 'something3cc'
    'something4' = 'something4dd'
    'something5' = 'something5dsf'
    'something6' = 'something6dfsfds'
}

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'

Get-Content -Path $original_file | ForEach-Object {
    $line = $_

    $lookupTable.GetEnumerator() | ForEach-Object {
        if ($line -match $_.Key)
        {
            $line = $line -replace $_.Key, $_.Value
        }
    }
   $line
} | Set-Content -Path $destination_file

Đây là cách tiếp cận tốt nhất mà tôi từng thấy cho đến nay. Vấn đề duy nhất là tôi phải đọc toàn bộ nội dung tệp cho một biến trước tiên để sử dụng cùng đường dẫn tệp nguồn / đích.
angularsen

đây có vẻ là câu trả lời tốt nhất, mặc dù tôi đã thấy một số hành vi kỳ lạ khi nó khớp không chính xác. tức là trong trường hợp bạn có bảng băm với các giá trị hex dưới dạng chuỗi (0x0, 0x1, 0x100, 0x10000) và 0x10000 sẽ khớp với 0x1.
Lorek

13

Với phiên bản 3 của PowerShell, bạn có thể xâu chuỗi các cuộc gọi thay thế với nhau:

 (Get-Content $sourceFile) | ForEach-Object {
    $_.replace('something1', 'something1').replace('somethingElse1', 'somethingElse2')
 } | Set-Content $destinationFile

Hoạt động tốt + hương vị thông thạo
hdoghmen

10

Giả sử bạn chỉ có thể có một 'something1'hoặc 'something2', v.v. trên mỗi dòng, bạn có thể sử dụng bảng tra cứu:

$lookupTable = @{
    'something1' = 'something1aa'
    'something2' = 'something2bb'
    'something3' = 'something3cc'
    'something4' = 'something4dd'
    'something5' = 'something5dsf'
    'something6' = 'something6dfsfds'
}

$original_file = 'path\filename.abc'
$destination_file =  'path\filename.abc.new'

Get-Content -Path $original_file | ForEach-Object {
    $line = $_

    $lookupTable.GetEnumerator() | ForEach-Object {
        if ($line -match $_.Key)
        {
            $line -replace $_.Key, $_.Value
            break
        }
    }
} | Set-Content -Path $destination_file

Nếu bạn có thể có nhiều hơn một trong số đó, chỉ cần xóa breaktrong ifcâu lệnh.


Tôi thấy TroyBramley đã thêm dòng $ ngay trước dòng cuối cùng để viết bất kỳ dòng nào không có thay đổi trong đó. Được chứ. Trong trường hợp của tôi, tôi chỉ thay đổi mỗi dòng cần thay thế.
vách đá

8

Tùy chọn thứ ba, đối với một lớp lót có đường ống là lồng các-chỗ đứng:

PS> ("ABC" -replace "B","C") -replace "C","D"
ADD

Và:

PS> ("ABC" -replace "C","D") -replace "B","C"
ACD

Điều này duy trì thứ tự thực thi, dễ đọc và nằm gọn trong một đường dẫn. Tôi thích sử dụng dấu ngoặc đơn để kiểm soát rõ ràng, tự lập tài liệu, v.v. Nó hoạt động mà không cần chúng, nhưng bạn tin tưởng điều đó đến đâu?

-Replace là một Toán tử so sánh, chấp nhận một đối tượng và trả về một đối tượng được cho là đã sửa đổi. Đây là lý do tại sao bạn có thể xếp chồng hoặc lồng chúng như hình trên.

Vui lòng xem:

help about_operators
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.