Prohledávání kódu: sgrep a spatch

Školení, která pořádám

Při programování velmi často prohledávám kód. Např. mám klávesovou zkratku na nalezení definice metody nebo naopak všech míst, ze kterých je volaná. Někdo k tomu používá IDE, řešení s vyhledáváním má výhodu, že je použitelné třeba i v případě, když jsou soubory uložené na vzdáleném serveru a IDE by se s jejich indexací po každé změně pěkně zapotilo. Kromě toho hledám i spoustu dalších věcí, kolikrát je třeba rychlejší vyhledat nějaký fragment textu ze stránky, než procházet adresáře k souboru, který ji generuje.

Ve Facebooku je repozitář tak velký, že i git grep používající index je nesnesitelně pomalý. Vznikla proto služba tbgs, která repozitář pravidelně indexuje do paměti několika serverů a hledá v nich distribuovaně.

Dobré nalezitelnosti jsem postupně začal přizpůsobovat i kód, který píšu. V první řadě je potřeba dodržovat štábní kulturu – pokud jednou napíšete funkci malými písmeny a podruhé velkými, tak musíte hledat bez rozlišování velikosti písmen a nejdete možná i to, co nechcete (třeba stejnojmennou konstantu). Pokud jednou napíšete závorku na stejný řádek a jednou na nový, tak to při hledání možná nezohledníte a najdete jen část toho, co hledáte. Stejné věci je dobré pojmenovávat stejně, různé různě. Např. pro CSS třídy používám jako oddělovač slov pomlčku, takže nekolidují s jinými identifikátory. Nalezitelnost přizpůsobuji i pojmenování metod – třeba místo obecného add metodu raději pojmenuji addComment, takže pak jdou snadno najít všechna místa, ze kterých je volaná.

Co ale dělat v případě, kdy hledáme něco složitějšího? Např. bychom chtěli najít všechny výskyty strncmp(A, B, strlen(B)) == 0 a změnit je na startsWith(A, B). Hračka, řeknete si – stačí hledat regulární výraz strncmp\(.*, strlen\( a s tím už si nějak poradíme. Může nastat nepřeberné množství komplikací:

Pokud je v kódu jen pár výskytů, tak toho můžeme najít víc a probrat se tím ručně. Pokud ale chceme změnit stovky výskytů nebo chceme mít změnu reprodukovatelnou, tak bychom potřebovali nástroj, který kódu rozumí na vyšší úrovni.

Sgrep

Přesně takovým nástrojem je sgrep. Ten postaví syntaktický strom zdrojového kódu a dovoluje hledat v něm. Poradí si tím pádem se všemi nástrahami, které v kódu můžou být – nečekané bílé znaky, komentáře, vnořené výrazy, zkrátka cokoliv. Použití z příkazové řádky je velmi jednoduché:

sgrep -e 'strncmp(A, B, strlen(B)) == 0'

Pochopitelnou nevýhodou je pomalost – sgrep musí všechny soubory zparsovat. Je možné ho proto použít s normálním grepem – nejprve si nahrubo vyfiltrujeme všechny soubory, které hledaný výraz určitě obsahují (a možná i některé další) a ty potom dohledáme sgrepem.

Spatch

Ještě mocnější nástroj je spatch. Ten soubory nejen prohledává, ale provádí v nich i změny. Změnu strncmp na startsWith lze provést tímto spatchem:

-strncmp
+startsWith
 (A, B
-, strlen(B)) == 0
+)

Závěr

Sgrep a spatch nepoužívám moc často, ale když už je použiji, tak jsem za ně velmi vděčný, protože bez nich bych úkol řešil mnohem pracněji. Je proto dobré o nich minimálně vědět.

Jakub Vrána, Seznámení s oblastí, 3.7.2013, diskuse: 8 (nové: 0)

Diskuse

ikona Ladislav Prskavec:

Zkousel jsi to porovnat s ack? Ten mi prijde pro zdrojove kody optimalnejsi nez grep.

ikona Jakub Vrána OpenID:

ack jsem nezkoušel. Nemám pocit, že by mě grep nějak omezoval, z příkazové řádky ho prakticky nepoužívám (buď použiji klávesovou zkratku pro hledání označeného slova nebo dialog v editoru). Takže si nejsem jist, co mi může ack nabídnout, ale možná ho zkusím.

nik:

Ahoj, mám dotaz mimo mísu, jakou distribuci linuxu používáš?

ikona Jakub Vrána OpenID:

Na serveru používám vždycky to, co už tam nainstaloval někdo jiný. Mám zkušenosti s Debianem (Centrum), Red Hatem (Facebook) a Ubuntu (AWS), nemám mezi nimi silnou preferenci. Raději mám distribuce, kde je pokud možno vždy to nejnovější (např. PHP 5.5 nebo OCaml 4 právě pro kompilaci sgrepu), což se mi bohužel zatím nepoštěstilo.

Ondra:

Možná by se hodilo poznamenat, že je to PHP only. Člověka to pak zbytečně navnadí :)

ikona Jakub Vrána OpenID:

Nicméně na https://github.com/facebook/pfff/wiki/Sgrep#wiki-synopsis se píše:

> For now only PHP is supported. Send an email to pad at fb.com if you want sgrep for your language.

V balíku PFFF je podpora pro několik dalších jazyků a přidat je i do sgrep a spatch by asi bylo relativně zvládnutelné.

Matlafous:

Jen otázka - co to má společného s http://www.cs.helsinki.fi/u/jjaakkol/sgrep.html ?

ikona Jakub Vrána OpenID:

Nic.

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.