PowerShell kullanarak bir dosyadaki birden çok dizeyi değiştirme


107

Bir yapılandırma dosyasını özelleştirmek için bir komut dosyası yazıyorum. Bu dosyadaki birden çok dize örneğini değiştirmek istiyorum ve işi yapmak için PowerShell'i kullanmayı denedim.

Tek bir değiştirme için iyi çalışır, ancak birden çok değiştirme yapmak çok yavaştır çünkü her seferinde tüm dosyayı yeniden ayrıştırması gerekir ve bu dosya çok büyüktür. Komut dosyası şöyle görünür:

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

Böyle bir şey istiyorum ama nasıl yazacağımı bilmiyorum:

$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

Yanıtlar:


167

Seçeneklerden biri, -replaceişlemleri birbirine zincirlemektir . `Her bir satır sonunda sonraki satırda ifade ayrıştırma devam PowerShell neden yeni satır kaçar:

$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

Diğer bir seçenek de bir ara değişken atamak olabilir:

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

$ Original_file == $ destination_file olabilir mi? Kaynağımla aynı dosyayı değiştiriyorum mu?
cquadrini

PowerShell cmdlet'lerinin girdi / çıktı akışını sağlama biçimi nedeniyle, aynı işlem hattındaki aynı dosyaya yazmak için işe yarayacağına inanmıyorum. Ancak, buna benzer bir şey yapabilirsiniz $c = Get-Content $original_file; $c | ... | Set-Content $original_file.
dahlbyk

Orijinal kodlamayı korumayan Set-Content kullanarak dosya kodlamayla ilgili sorunlarınız mı var ? Örneğin UTF-8 veya ANSI kodlamaları.
Kiquenet

1
Evet, PowerShell ... bu kadar yararsızdır. Kodlamayı kendiniz tespit etmelisiniz
dahlbyk

24

George Howarth'ın gönderiyi birden fazla yedekle düzgün bir şekilde çalışması için, sonu kaldırmanız, çıkışı bir değişkene ($ line) atamanız ve ardından değişkeni çıkarmanız gerekir:

$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

Şimdiye kadar gördüğüm en iyi yaklaşım bu. Tek sorun, aynı kaynak / hedef dosya yollarını kullanmak için önce tüm dosya içeriğini bir değişkene okumam gerekti.
angularsen

bu en iyi cevap gibi görünüyor, ancak bazı garip davranışlar gördüm, bununla yanlış eşleştiğini gördüm. Yani dizge olarak onaltılık değerlere sahip bir karma tablonuzun olması durumunda (0x0, 0x1, 0x100, 0x10000) ve 0x10000, 0x1 ile eşleşecektir.
Lorek

14

PowerShell sürüm 3 ile değiştirme çağrılarını birbirine bağlayabilirsiniz:

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

İyi çalışıyor + akıcı lezzet
hdoghmen

10

Her satıra yalnızca bir 'something1'veya 'something2'vb. Sahip olabileceğinizi varsayarsak , bir arama tablosu kullanabilirsiniz:

$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

Bunlardan birden fazlasına sahipseniz break, ififadeden kaldırmanız yeterlidir.


TroyBramley'in, üzerinde değişiklik olmayan herhangi bir satırı yazmak için son satırdan hemen önce $ satır eklediğini görüyorum. Tamam. Benim durumumda, yalnızca değiştirilmesi gereken her satırı değiştirdim.
cliffclof

8

Üçüncü bir seçenek, ardışık düzenlenmiş tek astar için-değiştiricileri iç içe yerleştirmektir:

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

Ve:

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

Bu, yürütme sırasını korur, okunması kolaydır ve bir boru hattına düzgün bir şekilde sığar. Açık kontrol, kendi kendini belgeleme vb. İçin parantez kullanmayı tercih ederim. Bunlar olmadan çalışır, ancak buna ne kadar güveniyorsunuz?

-Değiştir, bir nesneyi kabul eden ve muhtemelen değiştirilmiş bir nesne döndüren bir Karşılaştırma Operatörüdür. Bu yüzden onları yukarıda gösterildiği gibi istifleyebilir veya iç içe yerleştirebilirsiniz.

Bakınız:

help about_operators
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.