Karmaşık dizgiyi bulmak ve değiştirmek için sed'i kullanma (tercihen regex ile)


84

Aşağıdaki içeriğe sahip bir dosyam var:

<username><![CDATA[name]]></username>
<password><![CDATA[password]]></password>
<dbname><![CDATA[name]]></dbname>

ve ilk satırdaki "isim" i "şey" e, ikinci satırdaki "parola" "şey" ye, ve üçüncü satırdaki "isim" ye "farklı" a değiştirecek bir komut dosyası yapmam gerekiyor. Bu dosyada meydana gelenlerin sırasına güvenemiyorum, bu yüzden "isim" in ilk oluşumunu "bir şey" ile ve "isim" in ikinci oluşumunu "bir şey" ile değiştiremiyorum. Aslında doğru şeyi bulup değiştirdiğimden emin olmak için etraftaki dizeleri aramam gerekiyor.

Şimdiye kadar bu komutu ilk "isim" oluşumunu bulup değiştirmek için denedim:

sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml

ancak işe yaramadığından, bu karakterlerden bazılarının kaçmaya ihtiyaç duyabileceğini düşünüyorum.

İdeal olarak, iki "kullanıcı adı" olayını eşleştirmek ve yalnızca "adı" yerine koymak için regex'i kullanabilmeyi çok isterim. Böyle bir şey ama ile sed:

<username>.+?(name).+?</username>

ve parantez içindeki içerikleri "bir şey" ile değiştirin.

Mümkün mü?


2
Neredeyse bütünüyle regexp tabanlı bir çözümün, aksi belirtilmediği sürece, giriş formatı değiştiğinde riske girebileceğini unutmayın. Regexps, XML, SGML veya türevleri (bu bana bakar) ile başa çıkmak için kötü bir seçimdir.
CVn

Onaylandı! Örneğin, XQuery kullanmayı düşünün: w3schools.com/xquery/default.asp . Bu, XML içeriğini almak ve değiştirmek için kullanılan W3C standardıdır.
lgeorget

Yanıtlar:


157
sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml

Bu bence aradığın şey.

Açıklama:

  • birinci kısımdaki parantezler, ikinci kısımda tekrar kullanılabilecek grupları (aslında dizeleri) tanımlar.
  • \1, \2vb., ikinci kısımda, birinci kısımda yakalanan i-st grubuna yapılan atıflardır (numaralandırma 1 ile başlar).
  • -Egenişletilmiş düzenli ifadeler sağlar ( +gruplandırma için gereklidir ).

20
-E seçeneği için +1
slackmart

4
adıyla bir yedekleme dosyasının arkasında kalıyor (original name) + "-E".
Sarge Borsch

4
OSX'te 'sed: 1: "s / (<username>. +) Name (. + ...": \ 1, RE'de tanımlanmadı.) Bu sorudan tam örneği bir dosyaya yapıştırdım. Bu dosyadaki cevabın komutunu çalıştırdım, belki
OSX'in

1
Sed'in gnu sürümü "-E" parametresini destekler, ancak resmi değil. Manpage'de bile bahsedilmedi. Genişletilmiş regex kullanmak istiyorsanız, bunun yerine "-r" parametresini kullanmanız gerekir.
Ikem Krueger

3
@deweydb göre bu cevap , kullanmak gerekir \(ve \)yerine (ve ).
Zhang Buzz,

14
sed -e '/username/s/CDATA\[name\]/CDATA\[something\]/' \
-e '/password/s/CDATA\[password\]/CDATA\[somethingelse\]/' \
-e '/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/' file.txt

Daha /username/önce ssed 'in' username 'dizesini içeren satırlarda çalışmasını söyler.


1
Zarif, verimli ve kasa için mükemmel şekilde yerleştirilmiş. +1
lgeorget

6

sedZor bir gereklilik değilse , bunun yerine özel bir araç kullanın.

Dosyanız geçerli bir XML ise (yalnızca bu 3 XML görünümlü etiketi değil), XMLStarlet'i kullanabilirsiniz :

xml ed -P -O -L \
  -u '//username/text()' -v 'something' \
  -u '//password/text()' -v 'somethingelse' \
  -u '//dbname/text()' -v 'somethingdifferent' file.xml

Yukarıdakiler ayrıca düzenli ifadelerle çözülmesi zor olan durumlarda da çalışacaktır:

  • Mevcut değerleri belirtmeden etiketlerin değerlerini değiştirebilir.
  • Sadece kaçmış olsalar ve CDATA içine alınmamış olsalar bile değerleri değiştirebilirler.
  • Etiketlerin nitelikleri olsa bile değerleri değiştirebilir.
  • Aynı adda birden fazla varsa, sadece etiketlerin oluşumlarını kolayca değiştirebilirsiniz.
  • Değiştirilmiş XML'yi girintili olarak biçimlendirebilir.

Yukarıdakilerin kısa gösterimi:

bash-4.2$ cat file.xml
<sith>
<master>
<username><![CDATA[name]]></username>
</master>
<apprentice>
<username><![CDATA[name]]></username>
<password>password</password>
<dbname foo="bar"><![CDATA[name]]></dbname>
</apprentice>
</sith>

bash-4.2$ xml ed -O -u '//apprentice/username/text()' -v 'something' -u '//password/text()' -v 'somethingelse' -u '//dbname/text()' -v 'somethingdifferent' file.xml
<sith>
  <master>
    <username><![CDATA[name]]></username>
  </master>
  <apprentice>
    <username><![CDATA[something]]></username>
    <password>somethingelse</password>
    <dbname foo="bar"><![CDATA[somethingdifferent]]></dbname>
  </apprentice>
</sith>

3

Komutun \[.*^$/normal ifade bölümünde sve \&/değiştirme bölümünde artı newlines'ta alıntı yapmanız gerekir . Düzenli ifade, temel bir düzenli ifadedir ve ek olarak, skomut için sınırlayıcıyı alıntılamanız gerekir .

Alıntı yapmaktan kaçınmak için farklı bir sınırlayıcı seçebilirsiniz /. Bunun yerine o karakterden alıntı yapmanız gerekecek, ancak genellikle sınırlayıcıyı değiştirmenin amacı, değiştirilecek metinde ya da değiştirilecek metinde oluşmayan birini seçmektir.

sed -e 's~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~'

Yedek metindeki bazı parçaları tekrarlamaktan kaçınmak için grupları kullanabilir ve bu kısımlarda farklılıklar olabilir.

sed -e 's~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~'

sed -e 's~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~'

3
$ sed -e '1s/name/something/2' \
      -e '3s/name/somethingdifferent/2' \
      -e 's/password/somethingelse/2' sample.xml

Adresleri, satır numarasını belirten "s" den önceki sayıdaki gibi kullanabilirsiniz.

Ayrıca sondaki sayı sed, ilk eşleşmeyi değiştirmek yerine ikinci eşleşmeyi değiştirmeyi söyler .


1

"İsim" kelimesini "bir şey" kelimesi ile değiştirmek için şunu kullanın:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml

Bu, belirtilen kelimenin tüm tekrarlarının yerini alacak.

Şimdiye kadar hepsi standart çıktıya çıktı, şunları kullanabilirsiniz:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml

Değişiklikleri başka bir dosyaya kaydetmek için


0
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...

    -r, --regexp-extended
             use extended regular expressions in the script.

Böylece bir özellik dosyasındaki değeri değiştirmek için

sed -i -r 's/MAIL\=(.+)/MAIL\=user@mymail.com/' etc/service.properties 
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.