Mam plik o następującej zawartości:
<username><![CDATA[name]]></username> <password><![CDATA[password]]></password> <dbname><![CDATA[name]]></dbname>
i muszę zrobić skrypt, który zmieni„ nazwę ”w pierwszym wierszu na„ coś ”,„ hasło ”w drugim wierszu na„ coś innego ”, a„ nazwę ”w trzecia linijka to „coś innego”. Nie mogę polegać na kolejności ich występowania w pliku, więc nie mogę po prostu zamienić pierwszego wystąpienia „nazwa” na „coś”, a drugiego wystąpienia „nazwy” na „coś innego”. Właściwie muszę wyszukać otaczające ciągi, aby upewnić się, że znajduję i zastępuję właściwą rzecz.
Do tej pory próbowałem tego polecenia, aby znaleźć i zamienić pierwsze wystąpienie „nazwy”:
sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml
Jednak to nie działa, więc myślę, że niektóre z tych znaków mogą wymagać zmiany znaczenia itp.
Idealnie, ja ” Chciałbym móc używać wyrażenia regularnego, aby po prostu dopasować dwa wystąpienia „nazwy użytkownika” i zastąpić tylko „nazwę”. Coś takiego, ale z sed
:
<username>.+?(name).+?</username>
i zamień zawartość w nawiasach na „coś”.
Czy to możliwe?
Komentarze
- Zwróć uwagę, że prawie każde rozwiązanie oparte na wyrażeniach regularnych, chyba że wyjątkowo wymyślone, będzie przerywanie za każdym razem, gdy zmienia się format wejściowy. Wyrażenia regularne są kiepskim wyborem do obsługi XML, SGML lub pochodnych (co wydaje mi się).
- Zatwierdzone! Rozważ użycie XQuery na przykład: w3schools.com/xquery/default.asp . To jest standard W3C dotyczący pobierania i manipulowania treścią XML.
Odpowiedź
sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml
Myślę, że właśnie tego szukasz.
Wyjaśnienie:
- nawiasy w pierwszej części definiują grupy (w rzeczywistości łańcuchy), mogą być ponownie użyte w drugiej części
-
\1
,\2
itd. w drugiej części znajdują się odniesienia do i-ta grupa ujęta w pierwszej części (numeracja zaczyna się od 1) -
-E
włącza rozszerzone wyrażenia regularne (potrzebne dla+
i grupowanie).
Komentarze
- +1 dla opcji -E
- it pozostawia plik kopii zapasowej o nazwie
(original name) + "-E"
. - W systemie OSX otrzymuję ' sed: 1: ” s / (< nazwa użytkownika >. +) nazwa (. + … „: \ 1 nie zdefiniowane w RE '. Dokładny przykład z tego pytania wkleiłem do pliku. następnie uruchomiłem polecenie z tej odpowiedzi w tym pliku. Może OSX ma inną składnię?
- Wersja seda z gnu obsługuje parametr ” -E „, ale nie urzędnik. O tym ' nie ma nawet wzmianki na stronie podręcznika. Jeśli chcesz użyć rozszerzonego wyrażenia regularnego, musisz zamiast tego użyć parametru ” -r „.
- @ deweydb Zgodnie z tą odpowiedzią , należy użyć
\(
i\)
zamiast(
i)
.
Answer
sed -e "/username/s/CDATA\[name\]/CDATA\[something\]/" \ -e "/password/s/CDATA\[password\]/CDATA\[somethingelse\]/" \ -e "/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/" file.txt
/username/
przed s
informuje sed działa tylko na liniach zawierających ciąg „nazwa użytkownika”.
Komentarze
- Elegancki, wydajny i doskonale dopasowany do przypadku. +1
Odpowiedź
Jeśli sed
nie jest trudne wymaganie, lepiej zamiast tego użyj dedykowanego narzędzia.
Jeśli twój plik zawiera poprawny XML (a nie tylko te 3 tagi wyglądające na XML), możesz użyć XMLStarlet :
xml ed -P -O -L \ -u "//username/text()" -v "something" \ -u "//password/text()" -v "somethingelse" \ -u "//dbname/text()" -v "somethingdifferent" file.xml
Powyższe sprawdzi się również w sytuacjach, które byłyby trudne do rozwiązania za pomocą wyrażeń regularnych:
- Może zastąpić wartości tagów bez określania ich bieżących wartości.
- Może zastąpić wartości, nawet jeśli zawierają one tylko znaki zmiany znaczenia i nie są zawarte w CDATA.
- Może zastąpić wartości, nawet jeśli tagi mają atrybuty.
- Można łatwo zastąpić tylko wystąpienia tagów, jeśli jest ich wiele o tej samej nazwie.
- Może sformatować zmodyfikowany plik XML, wciskając go.
Krótka demonstracja powyższego:
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>
Odpowiedź
$ sed -e "1s/name/something/2" \ -e "3s/name/somethingdifferent/2" \ -e "s/password/somethingelse/2" sample.xml
Możesz po prostu użyć adresów a sw liczbie poprzedzającej „s”, która wskazuje numer linii.
Również liczba na końcu mówi sed
, aby zastąpić drugie dopasowanie zamiast zastępować pierwsze mecz.
Odpowiedź
Musisz zacytować \[.*^$/
w części wyrażenia regularnego s
polecenie i \&/
w części zamiennej oraz znaki nowej linii. Wyrażenie regularne jest podstawowym wyrażeniem regularnym , a ponadto należy umieścić w cudzysłowie separator dla polecenia s
.
Możesz wybrać inny separator, aby uniknąć cytowania /
. Zamiast tego trzeba będzie zacytować ten znak, ale zazwyczaj celem zmiany separatora jest wybranie takiego, który nie występuje ani w tekście do zastąpienia, ani w tekście zastępczym.
sed -e "s~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~"
Możesz użyć grup, aby uniknąć powtarzania niektórych części w tekście zastępczym i uwzględnić różnice w tych częściach.
sed -e "s~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~" sed -e "s~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~"
Odpowiedź
Aby zastąpić słowo „nazwa” słowem „coś”, użyj:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml
To zamieni wszystkie wystąpienia podanego słowa.
Jak dotąd wszystko jest wyprowadzane na standardowe wyjście, możesz użyć:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml
, aby zapisać zmiany w innym pliku.
Odpowiedź
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]... -r, --regexp-extended use extended regular expressions in the script.
aby zamienić wartość w pliku właściwości
sed -i -r "s/MAIL\=(.+)/MAIL\[email protected]/" etc/service.properties