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
Ivo Bosticky
sumber

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
dahlbyk.dll
sumber
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
TroyBramley
sumber
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
Ian Robertson
sumber
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.

George Howarth
sumber
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

sumber