Jak na hromadné nahrazování textu?

11. 5. 1999on-line nové okno domů předchozí článek následující článek

Určitě se vám již stalo, že jste potřebovali v několika souborech změnit text podle vám známého pravidla. Možná jste spustili svůj oblíbený textový editor a začali jste ručně nahrazovat výskyty textu XYZ textem ABC. Možná jste použili funkci Replace, zadali oba texty a pak jenom potvrzovali výměnu. Pokud těch souborů bylo několik (slovy méně než pět), pak se to dá zvládnout v rozumné době a při přijatelné námaze. Ale co když těch souborů je sto? Nebo padesát tisíc? Pro tento případ vám nabízím malý prográmek.

Tento prográmek se skládá ze dvou souborů - perlovského skriptu odstran a souboru s příkazy pro editor VI. Ne, nelekejte se, nebudete se muset učit pracovat s tímto klasickým editorem. Jen jej budeme využívat. V následujícím textu budu řešit problém odstranění absolutních adres v odkazech pro html soubory.

Skript
Perlovský skript je velice triviální. Vytvořte si soubor odstran (nebo použijte jiný název) a vložte do něj následující text: (tip pro začátečníky: tažením myši při stisknutém levém tlačítku text označte. Pak ve vedlejším okně či konzoli stisknutím prostředního tlačítka myši text vložíte do nějakého editoru.)

#!/usr/bin/perl
$soubory = `find . -name "*.html"`;
@file = split(/^/,$soubory);
foreach(@file)
{
        $neco = $_;
        chop $neco;
        print "Zpracovávám soubor $neco";
        system "vi -f -s /home/literakl/VI $neco";
}

Pak soubor uložte a nastavte u něj právo na spouštění (příkazem chmod +x odstran). Perlovští guru si možná budou rvát vlasy při pohledu na zdrojový kód nad jeho neefektivností, pro mně je ale důležité, že to funguje. Teď si jej trochu rozeberme. První řádek spouští interpretr perl. Pokud jej nemáte v adresáři /usr/bin, pak změňte cestu.

Na druhém řádku do proměnné $soubory vložíme všechny soubory, které našel program find. Tento program má obrovské možnosti, proto doporučuji přečíst si jeho manuálovou stránku (man find). Pro naše účely bude postačující následující popis.

První parametr určuje adresář, kde se má začít vyhledávat. V tomto případě tečka označuje aktuální adresář. Samozřejmě byste ale mohli použít absolutní cestu. Například /home/literakl/public_html/. Dalším parametrem je dvojice -name "*.html". Tento parametr určuje, že se vyberou všechny soubory, jejichž jméno splňuje druhá část podle shellovské konvence. Tedy můžete použít zástupné znaky jako jsou například hvězdička (zastupuje nulu a více libovolných znaků), otazník (zastupuje přesně jeden libovolný znak) a hranaté závorky ([aeiouy] zastupuje jeden z vypsaných znaků, v tomto případě samohlásky, [^aeiouy] zastupuje jeden znak, který není v uvedemé množině). Takže v tomto případě se vyberou všechny soubory končící na .html v aktuálním adresáři a všech jeho podadresářích.

Ve zbylé části skriptu se jedna proměnná rozdělí do pole promměných a v cyklu se pro každý nalezený soubor vypíše jeho jméno a zpracuje programem vi. Část za znakem < samozřejmě nahraďte správnou cestou.

Soubor s příkazy
Do tohoto souboru se vkládají příkazy pro editor vi. Nazvěte si jej a umístěte, jak a kam chcete. Nezapomeňte pak ale upravit předposlední řádek skriptu. Text souboru VI má obecně následující tvar:

:g/XYZ/s//ABC/g
:wq

Na prvním řádku je regulární výraz, na druhém příkaz pro zapsání změn a ukončení činnosti editoru vi. Pokud vám regulární výrazy nic neříkají, zkuste spustit vi a v něm napsat :help pattern a zmáčknout enter. Jinou možností je manuálová stránka regulárních výrazů pro perl - man perlre.

Při běžné práci nahradíte text XYZ hledaným textem a ABC novým textem. Pokud se vám nechce pročítat manuály k regulárním výrazům (vřele doporučuji je přečíst), snad vám pomůže následující výtah, který má daleko k úplnosti.

XYZ může být běžný text, který hledáte. Potom algoritmus porovnává prošlý text s požadovaným textem a nalezne-li shodu, změní jej na ABC. V XYZ ale mohou být zástupné znaky. Hvězdička stejně jako v shellu nahrazuje libovolný počet libovolných znaků. Tečka nahrazuje jeden libovolný znak; tj. má stejný význam jako otazník v shellu. Hranaté závorky se chovají také stejně jako v shellu, vybírají jeden platný znak z uvedené množiny. Pokud chcete najít znak *,.,[ nebo ], napište před ním zpětné lomítko. To ruší význam speciálního znaku.

Příklady:

Dalším speciální znakem je ^, pokud není použit jako negace ve výčtu znaků. Pak má význam začátku řádku. Obdobně znak $ ukazuje na konec řádku. Dvojice < je zase náhradou začátku slova a > pak konce slova.

Příklady

To by snad mohlo pro úvod stačit. Teď se vrhněme zpátky na náš příklad. V html souborech, které jsme si stáhli, autor používá absolutní odkazy i s názvem jeho serveru. Jenže my nejsme připojeni na internet a tak nám prohlížeč píše, že nemůže najít ten server. A navíc přístup na disk je rychlejší než přes internet (pokud nesedíte u TEN-6000 :) ). Takže potřebujete odstranit například řetězec http://www.domena.org/~uzivatel/adresar/ v několika desítkách souborů. Soubor VI pak bude mít následující tvar:

:g/HREF="http://www.domena.org/~uzivatel/adresar//s//HREF="../../g
:wq

Všimněte si zpětných lomítek rušících význam speciálních znaků. V části XYZ hledáme řetězec HREF="http://www.domena.org/~uzivatel/adresar/ a ten nahradíme řetězcem HREF=". Tímto způsobem nahradíme skutečně jen odkazy, nikoliv text. Po spuštění skriptu odstran ve správném adresáři se nahradí všechny výskyty a za několik sekund je tato práce hotova.

Varování

Regulární výrazy mohou dělat něco jiného, než jste zamýšleli. Proto, pokud nejste guru přes regulární výrazy, raději si pořiďte záložní kopii dat. Varoval jsem vás.

Nevýhody



Výhody Tento skript se hodí vždy, když potřebujete udělat nahrazení v mnoha souborech. Obvzláště se hodí při správě webovských stránek. Už se nebudete muset bát změny struktury adresářů na vašem rozlehlém webovském serveru. Pravda, něco podobného a příjemnějšího jsem nedávno viděl v Homesitu. Ani nevím, kdy to ode mne zkopírovali :). Snad to někomu pomůže.

Leoš Literák
literakl@seznam.cz
Linux Hardware

Autor: Leoš Literák (jiné články tohoto autora)
Sekce: Praxe
Související články:


Diskuse

11. 5. 1999 09:13:04 - Jde to i elegantneji. Misto '... (Petr Brouzda)
Jde to i elegantneji. Misto 'vi' pouzit 'sed' a je to na jednu dlouhou prikazovou radku zacinajici findem. PB.

11. 5. 1999 12:48:07 - Re: Jde to i elegantneji. Misto '... (Ondřej Solanský)
Nebo využít pro nahrazování jenom Perl: #!/usr/bin/perl while (glob('*.html')) { open (SOURCE,$_) or die '$!\n'; print 'Zpracovávám soubor ','$_,'\n'; open (TARGET, ')$_.new') or die '$!\n'; while ((SOURCE)) { chomp; s/nejaky text/za nejaky text/g; print TARGET $_,'\n'; }; close SOURCE; close TARGET; } while (glob('*.new')) { $old = $_; s/\.new//; rename $old,$_; }

11. 5. 1999 12:56:29 - Re: Jde to i elegantneji. Misto '... (Ondřej Solanský)
Poznámka : Na řádcích open (TARGET, ')$_.new') or die '$!\n'; while ((SOURCE)) { jsou vnitřní závorky ostré, nikoliv kulaté, za což nemohu - tak se to přeneslo.

11. 5. 1999 14:47:03 - Za vsechny, kteri nepouzivaji ... (Jakub Steiner)
Za vsechny, kteri nepouzivaji sed a byl pro ne tento clanek nesmirne uzitecnym dekuji autorovi.

11. 5. 1999 17:07:17 - Mam k tomu clanku par poznamek... (Robert Wolf)
Mam k tomu clanku par poznamek: A) zbytecne pouzivani perlu. stejne to lze udelat i v shellu for L in `find . -name *.html`;do sed -e '...';done jedna radka a je to OK (u toho find jsou zpetny apostrofy) a pokud to chcete jenom v aktualnim adresari, tak staci pouze sed -e '...' B) vetsina lidi asi pouziva Linux a obvykle RedHat nevim jak jinde ale na RH je vi akorat link na vim takze jestli ma vi a vim jiny regularni vyrazy, tak to nebude fungovat, navic vim ma trosku odlisne reg.vyrazy od perlu, takze pro perl se je ucte z man perlre a pro vim se je ucte z helpu vimu C) dalsi vec jsou priklady reg.vyrazu: vyraz ma*ka v zadnem pripade (ani ve vimu ani v perlu) nenajde slova matka, maska. totiz s reg.vyrazama je to asi takhle: . znak tecka zastupuje jakykoliv znak * udava, ze se PREDCHOZI znak muze opakovat 0 a vickrat + udeva opakovani PREDCHOZIHO znaku 1 a vicekrat ? udava opakovani PREDCHOZIHO znaku 1 nebo 0 krat {,n} predchozi znak se bude opakovat max. N-krat {m,n} predch.znak se musi opakovat minimalne M-krat a maximalne N-krat {m,} znak se musi opakovat minimalne M-krat {m} znak musi byt presne M-krat pokud chcete opakovat posloupnost treba ABC, tak se ta posloupnost uzavre do kulatych zavorek a za zavorku se prida pocet, napr. (ABC){3,5} najde posloupnost trojice ABC a to kdyz bude tahle trojice za sebou trikrat az petkrat, cili ABCABC ne ABCABCABC jo ABCABCABCABC jo ABCABCABCABCABC jo a delsi uz zase ne pokud chcete nechat hledat znaky *,+,(,),/ atd., musite pred ne vlozit znak \, znak / se pouziva na otevreni a uzavreni regularniho vyrazu, ale muzete misto nej pouzit i jiny znak a pak muzete v reg.vyrazu pouzit pouze znak / bez \. napr. pri hledani textu http:// je dobre pouzit vyraz !http://! , kde zavorky reg.vyrazu bude znak vykricnik je toho jeste vice, ale to by bylo opravdu na vlastni clanek. Tohle totiz plati u PERLu, VIM pouziva specialni znaky trosku jinak, napr. co v PERLu znamena znak +, to se ve VIMu musi napsat jako \+ atd. a take jeste zalezi na nastaveni magic. Prostudujte skutecne man perlre, man grep, man sed a help ve VIMu. D) navic tenhle vyraz ma dve chyby: g/HREF='http://www.domena.org/~uzivatel/adresar//s//HREF='/g 1) / nelze pouzit v reg.vyrazu, kdyz je / jako zavorky reg.vyrazu. Musi se bud pouzit jine zavorky nebo se pred znak / v reg.vyrazu musi zadat \ 2) znak ~ ma ve vimu specialni vyznam, takze taky potrebuje pred sebe znak \ E) pokud by nekdo chtel delat s vim a sed na MSDOS/WIN tak muze, staci jit na adresy ftp://ftp.vslib.cz/disk2/vim a sed je v ftp://ftp.zcu.cz/pub/simtelnet/gnu/djgpp/v2gnu/sed302b.zip Zdravim Wolf. P.S.: Sorry za preklepy a pripadne chyby, ale melo by to byt OK, ale nejsem regularni a VI guru:-) takze chybicka se mozna vloudi

11. 5. 1999 17:16:32 - Re: Mam k tomu clanku par poznamek... (Robert Wolf)
Tady to pokracuje!!!! Tohle totiz plati u PERLu, VIM pouziva specialni znaky trosku jinak, napr. co v PERLu znamena znak +, to se ve VIMu musi napsat jako \+ atd. a take jeste zalezi na nastaveni magic. Prostudujte skutecne man perlre, man grep, man sed a help ve VIMu. D) navic tenhle vyraz ma dve chyby: g/HREF='http://www.domena.org/~uzivatel/adresar//s//HREF='/g 1) / nelze pouzit v reg.vyrazu, kdyz je / jako zavorky reg.vyrazu. Musi se bud pouzit jine zavorky nebo se pred znak / v reg.vyrazu musi zadat \ 2) znak ~ ma ve vimu specialni vyznam, takze taky potrebuje pred sebe znak \ E) pokud by nekdo chtel delat s vim a sed na MSDOS/WIN tak muze, staci jit na adresy ftp://ftp.vslib.cz/disk2/vim a sed je v ftp://ftp.zcu.cz/pub/simtelnet/gnu/djgpp/v2gnu/sed302b.zip Zdravim Wolf. P.S.: Sorry za preklepy a pripadne chyby, ale melo by to byt OK, ale nejsem regularni a VI guru:-) takze chybicka se mozna vloudi

11. 5. 1999 17:24:16 - Re: Mam k tomu clanku par poznamek... (Robert Wolf)
A vloudila se chybicka: V bode A) jsem chtel rict, ze pomoci sedu pouze v aktualnim adresari to jde pomoci prikazu sed -e '...' *.html kde ... je reg.vyraz neco jako s!HREF='http;//www.server.org/\~uzivatel/!HREF='!gi to g na konci znamena globalne-vickrat v jedne radce, coz se muze stat, ze na jedne radce bude vice odkazu a to i znamena ignore-case, coz je dost dobre, protoze pokud nekdo zapise http misto HTTP, tak uz by to nas stary regularni vyraz nenasel Wolf


on-line nové okno domů předchozí článek následující článek