Použití sed k nalezení a nahrazení složitého řetězce (nejlépe regexem)

Mám soubor s následujícím obsahem:

a musím vytvořit skript, který změní„ název “v prvním řádku na„ něco “,„ heslo “v druhém řádku na„ něco jiného “a„ název “v třetí řádek na „něco jiného“. Nemohu se spoléhat na pořadí, v jakém se v souboru vyskytují, takže nemohu jednoduše nahradit první výskyt „jména“ „něčím“ a druhý výskyt „jména“ „něčím odlišným“. Vlastně musím vyhledat okolní řetězce, abych se ujistil, že hledám a nahradím správnou věc.

Zatím jsem tento příkaz zkusil najít a nahradit první výskyt „jména“:

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

nicméně to nefunguje, takže si myslím, že některé z těchto postav možná budou muset uniknout atd.

V ideálním případě já “ Rád bych mohl používat regex pouze ke shodě se dvěma výskyty „uživatelského jména“ a nahradit pouze „jméno“. Něco takového, ale s sed:

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

a nahradit obsah v závorkách za „něco“.

Je to možné?

Komentáře

  • Vezměte na vědomí, že jakékoli řešení založené na regexp, pokud nebude extrémně vymyšlené, bude riskovat kdykoli dojde ke změně vstupního formátu. Regexps je špatná volba pro práci s XML, SGML nebo deriváty (což se mi zdá).
  • Schváleno! Zvažte například použití XQuery: w3schools.com/xquery/default.asp . Toto je standard W3C pro načítání a manipulaci s obsahem XML.

Odpověď

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

To je, myslím, to, co hledáte.

Vysvětlení:

  • závorky v první části definují skupiny (ve skutečnosti řetězce), které lze znovu použít ve druhé části
  • \1, \2 atd. ve druhé části jsou odkazy na i-ta skupina zachycená v první části (číslování začíná 1)
  • -E umožňuje rozšířené regulární výrazy (potřebné pro + a seskupení).

Komentáře

  • +1 pro možnost -E
  • it zanechá za sebou záložní soubor s názvem (original name) + "-E".
  • Na OSX dostanu ‚ sed: 1: “ s / (< uživatelské jméno >. +) name (. + … „: \ 1 ne definované v RE ‚. Přesný příklad z této otázky jsem vložil do souboru. pak jsem spustil příkaz z této odpovědi na tento soubor. Možná má OSX jinou syntaxi?
  • Verze gnu sed podporuje parametr “ -E „, ale ne oficiální. Na stránce to není ‚ ani zmíněno. Pokud chcete použít rozšířený regulární výraz, musíte místo toho použít parametr “ -r „.
  • @ deweydb Podle této odpovědi byste měli použít \( a \) místo ( a ).

Odpovědět

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/ před s říká sed pracovat pouze na řádcích obsahujících řetězec „username“.

Komentáře

  • Elegantní, efektivní a perfektně přizpůsobený případu. +1

Odpověď

Pokud sed není tvrdý požadavek, raději použijte vyhrazený nástroj.

Pokud je váš soubor platný XML (nejen tyto 3 tagy vypadající XML), můžete použít XMLStarlet :

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

Výše uvedené bude fungovat také v situacích, které by bylo obtížné vyřešit pomocí regulárních výrazů:

  • Může nahradit hodnoty značek bez zadání jejich aktuálních hodnot.
  • Může nahradit hodnoty, i když právě unikly a nejsou uzavřeny v CDATA.
  • Může nahradit hodnoty, i když značky mají atributy.
  • Lze snadno nahradit pouze výskyty značek, pokud jich je více se stejným názvem.
  • Upravený XML lze formátovat odsazením.

Stručná ukázka výše uvedeného:

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> 

Odpověď

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

Můžete jednoduše použít adresy a s v čísle předcházejícím „s“, které označuje číslo řádku.

Také číslo na konci říká sed nahradit druhou shodu místo nahrazení první zápas.

Odpověď

Musíte uvést \[.*^$/ v části regulárního výrazu Příkaz s a \&/ v náhradní části plus nové řádky. Regulární výraz je základní regulární výraz a navíc je třeba uvést oddělovač pro příkaz s.

Můžete si vybrat jiný oddělovač, abyste nemuseli citovat /. Místo toho budete muset citovat tento znak, ale obvykle je třeba změnit oddělovač tak, že vyberete ten, který se nevyskytuje ani v nahrazovaném textu, ani v náhradním textu.

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

Pomocí skupin můžete zabránit opakování některých částí v náhradním textu a přizpůsobit je různým částem.

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

Odpověď

Chcete-li slovo „name“ nahradit slovem „something“, použijte:

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

To nahradí všechny výskyty zadaného slova.

Zatím je vše na standardní výstup, můžete použít:

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

uložení změn do jiného souboru.

odpověď

Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]... -r, --regexp-extended use extended regular expressions in the script. 

tak, aby se nahradila hodnota v souboru vlastností

sed -i -r "s/MAIL\=(.+)/MAIL\[email protected]/" etc/service.properties 

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *