Cara mengganti beberapa string dalam file menggunakan PowerShell


107

Saya menulis skrip untuk menyesuaikan file konfigurasi. Saya ingin mengganti beberapa contoh string dalam file ini, dan saya mencoba menggunakan PowerShell untuk melakukan pekerjaan itu.

Ini berfungsi dengan baik untuk satu penggantian, tetapi melakukan banyak penggantian sangat lambat karena setiap kali harus mengurai seluruh file lagi, dan file ini sangat besar. Skripnya terlihat seperti ini:

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

Saya menginginkan sesuatu seperti ini, tetapi saya tidak tahu cara menulisnya:

$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

Jawaban:


167

Salah satu opsinya adalah -replacemenyatukan operasi. Di `akhir setiap baris lolos dari baris baru, menyebabkan PowerShell untuk terus mengurai ekspresi di baris berikutnya:

$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

Pilihan lain adalah menetapkan variabel perantara:

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

Bisakah $ original_file == $ destination_file? Seperti pada saya memodifikasi file yang sama dengan sumber saya?
cquadrini

Karena cara cmdlet PowerShell mengalirkan input / ouput mereka, saya tidak percaya akan berhasil jika menulis ke file yang sama di pipeline yang sama. Namun, Anda bisa melakukan sesuatu seperti $c = Get-Content $original_file; $c | ... | Set-Content $original_file.
dahlbyk

Apakah Anda mengalami masalah tentang pengkodean file menggunakan Set-Content yang tidak mempertahankan pengkodean aslinya? Encoding UTF-8 atau ANSI misalnya.
Kiquenet

1
Ya, PowerShell ... tidak membantu seperti itu. Anda harus mendeteksi encoding sendiri, misalnya github.com/dahlbyk/posh-git/blob/...
dahlbyk

24

Agar posting oleh George Howarth berfungsi dengan baik dengan lebih dari satu pengganti, Anda perlu menghapus jeda, tetapkan output ke variabel ($ line) dan kemudian keluarkan variabel:

$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

Sejauh ini, ini adalah pendekatan terbaik yang pernah saya lihat. Satu-satunya masalah adalah saya harus membaca seluruh konten file ke variabel terlebih dahulu untuk menggunakan jalur file sumber / tujuan yang sama.
angularsen

ini sepertinya jawaban terbaik, meskipun saya telah melihat beberapa perilaku aneh dengan pencocokan yang tidak tepat. yaitu dalam kasus di mana Anda memiliki tabel hash dengan nilai hex sebagai string (0x0, 0x1, 0x100, 0x10000) dan 0x10000 akan cocok dengan 0x1.
Lorek

13

Dengan PowerShell versi 3, Anda dapat menyatukan panggilan ganti:

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

Bekerja dengan baik + rasa fasih
hdoghmen

10

Dengan asumsi Anda hanya dapat memiliki satu 'something1'atau 'something2', dll. Per baris, Anda dapat menggunakan tabel pencarian:

$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

Jika Anda dapat memiliki lebih dari satu, hapus saja breakdi ifpernyataan.


Saya melihat TroyBramley menambahkan $ baris tepat sebelum baris terakhir untuk menulis baris apa pun yang tidak memiliki perubahan di dalamnya. Baik. Dalam kasus saya, saya hanya mengubah setiap baris yang perlu diganti.
cliffclof

8

Opsi ketiga, untuk satu-liner pipelined adalah menumpuk -replaces:

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

Dan:

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

Ini mempertahankan urutan eksekusi, mudah dibaca, dan cocok dengan pipa. Saya lebih suka menggunakan tanda kurung untuk kontrol eksplisit, dokumentasi diri, dll. Ini berfungsi tanpa tanda kurung, tetapi seberapa jauh Anda mempercayainya?

-Replace adalah Operator Perbandingan, yang menerima objek dan mengembalikan objek yang mungkin dimodifikasi. Inilah mengapa Anda dapat menumpuk atau menumpuknya seperti yang ditunjukkan di atas.

Silahkan lihat:

help about_operators
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.