Birden fazla deseni aynı anda sed ile nasıl değiştirebilirim?


231

Diyelim ki 'abbc' dizesi var ve değiştirmek istiyorum:

  • ab -> m.ö.
  • bc -> ab

Ben iki yerine denerseniz sonuç istediğim değil:

echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g'
abab

Peki aşağıdaki gibi değiştirmek için hangi sed komutunu kullanabilirim?

echo abbc | sed SED_COMMAND
bcab

EDIT : Aslında metin 2'den fazla desen olabilir ve kaç yerine ihtiyacım olacağını bilmiyorum. sedBir akış editörü olduğunu söyleyen bir cevap vardı ve onun yerine açgözlülükle bence bunun için bir komut dosyası dili kullanmam gerekecek.


Aynı hatta birden fazla değişiklik yapmanız mı gerekiyor? Değilse sadece gbu s///komutların her ikisinden de bayrak bırakın ve bu işe yarayacaktır.
Etan Reisner

Sorumun amacını kaçırdın. Demek istediğim, her bir değiştirme işlemini aynı hatta bir kereden fazla yapmanız gerekiyor . Orijinal giriş için ab veya bc orijinal girişte birden fazla eşleşme var mı ?
Etan Reisner

Üzgünüm @EtanReisner yanlış anladım, Anwser evet. metinde birden fazla değişiklik olabilir.
DaniloNC

Yanıtlar:


342

Belki böyle bir şey:

sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'

Dizede ~olmayacağını bildiğiniz bir karakterle değiştirin .


9
GNU kolları NULs sed, bu nedenle kullanabilirsiniz \x0için ~~.
jthill

3
ggerekli ve ne yapar?
Lee

12
@Lee gglobal içindir - yalnızca ilk satır yerine (varsayılan davranış olan) her satırdaki desenin tüm örneklerini değiştirir.
naught101

1
Birden fazla kombinasyonu aynı anda değiştirebilecek ooga cevabının bir çeşidi için lütfen cevabım stackoverflow.com/a/41273117/539149 adresine bakın .
Zack Morris

3
Eğer dizede olmayacak biliyorum üretim kodu için giriş hakkında herhangi bir varsayım yapmak asla yoktur. Testler için, testler asla gerçekten doğruluğu kanıtlamaz, ancak bir test için iyi bir fikir şudur: Komut dosyasının kendisini girdi olarak kullanın.
hagello

33

Her zaman "-e" ile birden fazla ifade kullanırım

$ sed -e 's:AND:\n&:g' -e 's:GROUP BY:\n&:g' -e 's:UNION:\n&:g' -e 's:FROM:\n&:g' file > readable.sql

Bu, 'AND', AND'lerin, GROUP BY'ların, UNION'ların ve FROM'ların önüne eklenirken, '&' eşleşen dizeyi ve '\ n &' eşleşen dizeyi '\ n' ile eşleşmeden önce '\ n' ile değiştirmek istediğiniz anlamına gelir. '


14

Aşağıda, değerlerin nasıl yeniden kullanılabileceğini kontrol etmek zorunda kalmadan, çoklu arama ve değiştirme çiftleri için çalışan ooga yanıtında bir varyasyon bulunmaktadır :

sed -i '
s/\bAB\b/________BC________/g
s/\bBC\b/________CD________/g
s/________//g
' path_to_your_files/*.txt

İşte bir örnek:

önce:

some text AB some more text "BC" and more text.

sonra:

some text BC some more text "CD" and more text.

Unutmayın ki \bkelime sınırlarını belirtir, bu ________da aramaya müdahale etmesini önler (Ubuntu üzerinde GNU sed 4.2.2 kullanıyorum). Kelime sınır araması kullanmıyorsanız, bu teknik çalışmayabilir.

Bunun , komutun kaldırılması s/________//gve sonuna eklenmesi && sed -i 's/________//g' path_to_your_files/*.txtile aynı sonuçları verdiğini , ancak yolun iki kez belirtilmesini gerektirmediğini de unutmayın.

Bununla ilgili genel bir varyasyon, jthill'in önerdiği gibi dosyalarınızda hiçbir boş değer görünmediğini biliyorsanız kullanmak \x0veya _\x0_yerine ________olacaktır .


Yukarıdaki hagello'nun girdinin ne içerebileceği konusunda varsayımlarda bulunmama hakkındaki yorumuna katılıyorum. Bu nedenle, şahsen bunun en güvenilir çözüm olduğunu hissediyorum, yanı sıra boru seds birbiri üzerine ( sed 's/ab/xy/' | sed 's/cd/ab/' .....)
leetbacoon

12

sedbir akış düzenleyicisidir. Açgözlülükle arar ve değiştirir. İstediğinizi yapmanın tek yolu, bir ara ikame kalıbı kullanmak ve sonunda geri değiştirmek.

echo 'abcd' | sed -e 's/ab/xy/;s/cd/ab/;s/xy/cd/'


4

Bu sizin için işe yarayabilir (GNU sed):

sed -r '1{x;s/^/:abbc:bcab/;x};G;s/^/\n/;:a;/\n\n/{P;d};s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/;ta;s/\n(.)/\1\n/;ta' file

Bu, bekleme alanında (HS) hazırlanan ve tutulan ve daha sonra her satıra eklenen bir arama tablosu kullanır. Benzersiz bir işaretleyici (bu durumda \n) çizginin başlangıcına eklenir ve çizginin uzunluğu boyunca arama boyunca çarpma yöntemi olarak kullanılır. İşaretçi çizginin sonuna ulaştığında işlem tamamlanır ve arama tablosu ve işaretler atılır.

Not: Arama tablosu en başında hazırlanır :ve ikame dizeleriyle çakışmayacak şekilde ikinci bir benzersiz işaretçi (bu durumda ) seçilir.

Bazı yorumlarla:

sed -r '
  # initialize hold with :abbc:bcab
  1 {
    x
    s/^/:abbc:bcab/
    x
  }

  G        # append hold to patt (after a \n)

  s/^/\n/  # prepend a \n

  :a

  /\n\n/ {
    P      # print patt up to first \n
    d      # delete patt & start next cycle
  }

  s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/
  ta       # goto a if sub occurred

  s/\n(.)/\1\n/  # move one char past the first \n
  ta       # goto a if sub occurred
'

Tablo şu şekilde çalışır:

   **   **   replacement
:abbc:bcab
 **   **     pattern

3

Aşağıdaki gibi deneyebileceğiniz tek bir kalıp oluşumu için daha basit bir yaklaşım olabilir: echo 'abbc' | sed 's / ab / bc /; s / bc / ab / 2'

Çıktım:

 ~# echo 'abbc' | sed 's/ab/bc/;s/bc/ab/2'
 bcab

Çoğul kalıp oluşumları için:

sed 's/\(ab\)\(bc\)/\2\1/g'

Misal

~# cat try.txt
abbc abbc abbc
bcab abbc bcab
abbc abbc bcab

~# sed 's/\(ab\)\(bc\)/\2\1/g' try.txt
bcab bcab bcab
bcab bcab bcab
bcab bcab bcab

Bu yardımcı olur umarım !!


2

Tcl bir sahiptir yerleşiğini bunun için

$ tclsh
% string map {ab bc bc ab} abbc
bcab

Bu, geçerli konumdan başlayarak dize karşılaştırmaları yaparak bir defada bir karakteri yürüterek çalışır.

Perl dilinde:

perl -E '
    sub string_map {
        my ($str, %map) = @_;
        my $i = 0;
        while ($i < length $str) {
          KEYS:
            for my $key (keys %map) {
                if (substr($str, $i, length $key) eq $key) {
                    substr($str, $i, length $key) = $map{$key};
                    $i += length($map{$key}) - 1;
                    last KEYS;
                }
            }
            $i++;
        }
        return $str;
    }
    say string_map("abbc", "ab"=>"bc", "bc"=>"ab");
'
bcab

0

İşte bir awkoogas dayanmaktadırsed

echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab
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.