11. 5. 1999 | ![]() ![]() ![]() ![]() ![]() |
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:
Příklady
: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
Leoš Literák
literakl@seznam.cz
Linux Hardware
Autor: Leoš Literák (jiné články tohoto autora)
Sekce: Praxe
Související články:
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
![]() ![]() ![]() ![]() ![]() |