Nedávno jsem začal spolupracovat s Asciidoctor.js a on projekt Asciidoctor.js-pug a Asciidoctor-templates.js.
Není vždy snadné být okamžitě efektivní, když se poprvé ponoříte do kódové báze obsahující několik tisíc řádků. Ale mou tajnou zbraní, jak najít cestu přes tolik řádků kódu, je grep
nástroj.
Podělím se s vámi o použití příkazu grep v Linuxu s příklady.
Reálné užitečné příklady příkazů grep v Linuxu
Pokud se podíváte do man
, uvidíte krátký popis grep
nástroj:„tisknout čáry odpovídající vzoru.“
Nenechte se však zmást takovou skromnou definicí:grep
je jedním z nejužitečnějších nástrojů v unixové sadě nástrojů a existuje nespočet příležitostí k jeho použití, jakmile pracujete s textovými soubory.
Vždy je lepší mít příklady z reálného světa, abyste se naučili, jak věci fungují. Použiji tedy zdrojový strom Asciidoctor.js k ilustraci některých grep
schopnosti.
Tento zdrojový strom si můžete stáhnout z GitHubu, a pokud chcete, můžete se dokonce podívat na stejnou sadu změn, kterou jsem použil při psaní tohoto článku. To zajistí, že získáte výsledky naprosto identické s těmi, které jsou popsány ve zbytku tohoto článku:
git clone https://github.com/asciidoctor/asciidoctor.js
cd asciidoctor.js
git checkout v1.5.6-rc.1
1. Najít všechny výskyty řetězce (základní použití)
Asciidoctor.js podporuje Nashorn JavaScript engine pro platformu Java. Neznám Nashorn, takže bych mohl využít této příležitosti a dozvědět se o něm více prozkoumáním částí projektu odkazujících na tento JavaScript engine.
Jako výchozí bod jsem zkontroloval, zda v package.json
nejsou nějaká nastavení související s Nashornem soubor popisující závislosti projektu:
[email protected]:~$ grep nashorn package.json
"test": "node npm/test/builder.js && node npm/test/unsupported-features.js && node npm/test/jasmine-browser.js && node npm/test/jasmine-browser-min.js && node npm/test/jasmine-node.js && node npm/test/jasmine-webpack.js && npm run test:karmaBrowserify && npm run test:karmaRequirejs && node npm/test/nashorn.js",
Ano, zjevně tam byly nějaké testy specifické pro Nashorn. Pojďme to tedy prozkoumat trochu více.
2. Vyhledávání v sadě souborů bez rozlišení malých a velkých písmen
Nyní se chci blíže podívat na soubory z ./npm/test/
adresář výslovně zmiňuje Nashorn.
Vyhledávání bez ohledu na velikost písmen (-i
option) je zde pravděpodobně lepší, protože potřebuji najít oba odkazy na nashorn
a Nashorn
(nebo jakákoli jiná kombinace velkých a malých písmen):
[email protected]:~$ grep -i nashorn npm/test/*.js
npm/test/nashorn.js:const nashornModule = require('../module/nashorn');
npm/test/nashorn.js:log.task('Nashorn');
npm/test/nashorn.js:nashornModule.nashornRun('jdk1.8.0');
Zde byla skutečně užitečná necitlivost na malá a velká písmena. Jinak bych vynechal require('../module/nashorn')
prohlášení. Nepochybně bych měl ten soubor prozkoumat podrobněji později.
3. Najděte všechny neodpovídající soubory
Mimochodem, jsou v npm/test/
nějaké soubory, které nejsou specifické pro Nashorm adresář? K zodpovězení této otázky můžeme použít možnost „tisknout neodpovídající soubory“ grep (-L
možnost):
sh$ grep -iL nashorn npm/test/*
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js
Všimněte si, jak pomocí -L
zvolte výstup grep
se změnil tak, aby zobrazoval pouze názvy souborů. Žádný z výše uvedených souborů tedy neobsahuje řetězec „nashorn“ (bez ohledu na velikost písmen). To neznamená, že s touto technologií nějak nesouvisí, ale alespoň tam nejsou písmena „n-a-s-h-o-r-n“.
4. Hledání vzorů do skrytých souborů a rekurzivně do podadresářů
Poslední dva příkazy používaly vzor shell glob k předání seznamu souborů k prozkoumání do grep
příkaz.
Má to však určitá inherentní omezení:hvězdička (*
) nebude odpovídat skrytým souborům. Ani to nebude odpovídat souborům (nakonec) obsaženým v podadresářích.
Řešením by bylo zkombinovat grep
pomocí příkazu find namísto spoléhání se na vzor shell glob:
# This is not efficient as it will spawn a new grep process for each file
[email protected]:~$ find npm/test/ -type f -exec grep -iL nashorn \{} \;
# This may have issues with filenames containing space-like characters
[email protected]:~$ grep -iL nashorn $(find npm/test/ -type f)
Jak jsem to zmínil v komentářích k bloku kódu výše, každé z těchto řešení má nevýhody.
Pokud jde o názvy souborů obsahující znaky podobné mezerám, nechám vás prozkoumat grep -z
možnost, která v kombinaci s -print0
možnost find
příkaz, může tento problém zmírnit. Neváhejte a použijte sekci komentářů na konci tohoto článku a podělte se o své nápady na toto téma!
Nicméně lepším řešením by bylo použití „rekurzivního“ (-r
) možnost grep
. S touto volbou zadáte na příkazovém řádku kořen vašeho vyhledávacího stromu (počáteční adresář) namísto explicitního seznamu jmen souborů k prozkoumání.
Pomocí -r
grep prohledá všechny soubory v zadaném adresáři, včetně skrytých, a poté rekurzivně sestoupí do libovolného podadresáře:
[email protected]:~$ grep -irL nashorn npm/test/npm/
npm/test/builder.js
npm/test/jasmine-browser-min.js
npm/test/jasmine-browser.js
npm/test/jasmine-node.js
npm/test/jasmine-webpack.js
npm/test/unsupported-features.js
Vlastně s touto možností bych také mohl začít svůj průzkum o úroveň výše, abych viděl, že existují testy bez NPM, které cílí i na Nashorn:
[email protected]:~$ grep -irL nashorn npm/
Nechal jsem vás otestovat tento příkaz sami, abyste viděli jeho výsledek; ale jako nápovědu mohu říci, že byste měli najít mnohem více odpovídajících souborů!
5. Filtrování souborů podle jejich názvu (pomocí regulárních výrazů)
Zdá se tedy, že v tomto projektu jsou nějaké testy specifické pro Nashorn. Vzhledem k tomu, že Nashorn je Java, další otázka, která by mohla být vznesena, by byla „existují v projektu nějaké zdrojové soubory Java, které výslovně zmiňují Nashorn?“ .
V závislosti na verzi grep
pro odpověď na tuto otázku existují alespoň dvě řešení.
První z nich je použití grep
k nalezení všech souborů obsahujících vzor „nashorn“, pak výstup tohoto prvního příkazu přesměrujte do druhého grep
instance filtrující zdrojové soubory jiné než Java:
[email protected]:~$ grep -ir nashorn ./ | grep "^[^:]*\.java"
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/basic.js"));
První polovina příkazu by již měla být srozumitelná. Ale co ta část „^[\^:]*\\.java“?
Pokud nezadáte -F
možnost, grep
předpokládá, že vyhledávací vzor je regulární výraz. To znamená, že kromě jednoduchých znaků, které budou odpovídat doslovně, máte přístup k sadě metaznaků pro popis složitějších vzorů. Vzor, který jsem použil výše, bude odpovídat pouze:
^
začátek řádku[^:]*
následuje sekvence libovolných znaků kromě dvojtečky\.
následuje tečka (tečka má v regulárním výrazu zvláštní význam , takže jsem to musel chránit zpětným lomítkem, abych vyjádřil, že chci doslovnou shodu)java
a následují čtyři písmena „java.“
V praxi od grep
použije dvojtečku k oddělení názvu souboru od kontextu, ponechám pouze řádky s .java
v sekci název souboru. Stojí za zmínku by odpovídat také .javascript
názvy souborů. To je něco, co jsem nechal zkusit vyřešit sám, pokud chcete.
6. Filtrování souborů podle jejich názvu pomocí grep
Regulární výrazy jsou extrémně silné. V tomto konkrétním případě se to však zdá být přehnané. Nemluvě o výše uvedeném řešení, trávíme čas zkoumáním všech souborů při hledání vzoru „nashorn“ – většina výsledků je zahozena ve druhém kroku potrubí.
Pokud používáte GNU verzi grep
, což je pravděpodobné, pokud používáte Linux, ale máte jiné řešení s --include
volba. To dává pokyn grep
pro vyhledávání pouze v souborech, jejichž název odpovídá danému vzoru glob:
[email protected]:~$ grep -ir nashorn ./ --include='*.java'
./spec/nashorn/AsciidoctorConvertWithNashorn.java:public class AsciidoctorConvertWithNashorn {
./spec/nashorn/AsciidoctorConvertWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/AsciidoctorConvertWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/asciidoctor-convert.js"));
./spec/nashorn/BasicJavascriptWithNashorn.java:public class BasicJavascriptWithNashorn {
./spec/nashorn/BasicJavascriptWithNashorn.java: ScriptEngine engine = engineManager.getEngineByName("nashorn");
./spec/nashorn/BasicJavascriptWithNashorn.java: engine.eval(new FileReader("./spec/nashorn/basic.js"));
7. Hledání slov
Zajímavostí projektu Asciidoctor.js je, že jde o vícejazyčný projekt. Ve svém jádru je Asciidoctor napsán v Ruby, takže aby byl použitelný ve světě JavaScriptu, musí být „transpilován“ pomocí Opal, překladače mezi zdroji z Ruby do JavaScriptu. Další technologie, o které jsem předtím nevěděl.
Po prozkoumání specifik Nashornu jsem si tedy zadal úkol lépe porozumět Opal API. Jako první krok v tomto questu jsem prohledal všechny zmínky o Opal
globální objekt v souborech JavaScript projektu. Může se objevit v afektech (Opal =
), členský přístup (Opal.
) nebo možná i v jiných kontextech. Regulární výraz by stačil. Ještě jednou však grep
má nějaké lehčí řešení k vyřešení tohoto běžného případu použití. Pomocí -w
možnost, bude odpovídat pouze slovům , tedy vzory, kterým předchází a za nimi následuje neslovní znak. Neslovný znak je buď začátek řádku, konec řádku nebo jakýkoli znak, který není ani písmenem, ani číslicí, ani podtržítkem:
[email protected]:~$ grep -irw --include='*.js' Opal .
...
8. obarvení výstupu
Nekopíroval jsem výstup předchozího příkazu, protože existuje mnoho shod. Když je výstup takto hustý, možná budete chtít přidat trochu barvy pro usnadnění porozumění. Pokud to ještě není ve vašem systému nakonfigurováno ve výchozím nastavení, můžete tuto funkci aktivovat pomocí GNU --color
možnost:
[email protected]:~$ grep -irw --color=auto --include='*.js' Opal .
...
Měli byste získat stejně dlouhý výsledek jako dříve, ale tentokrát by se hledaný řetězec měl zobrazit barevně, pokud tomu tak již nebylo.
9. Počítání odpovídajících řádků nebo odpovídajících souborů
Dvakrát jsem zmínil, že výstup předchozích příkazů byl velmi dlouhý. Jak dlouho přesně?
[email protected]:~$ grep -irw --include='*.js' Opal . | wc -l
86
To znamená, že máme celkem 86 odpovídajících řádků ve všech zkoumané soubory. Kolik různých souborů se však shoduje? Pomocí -l
možnost můžete omezit grep
výstup odpovídajících souborů místo zobrazení odpovídajících řádků . Takže tato jednoduchá změna řekne, kolik souborů se shoduje:
[email protected]:~$ grep -irwl --include='*.js' Opal . | wc -l
20
Pokud vám to připomíná -L
možnost, žádné překvapení:jak je poměrně běžné, k rozlišení doplňkových možností se používají malá/velká písmena. -l
zobrazí odpovídající názvy souborů. -L
zobrazí neodpovídající názvy souborů. Jako další příklad vám dovolím zkontrolovat manuál pro -h
/-H
možnosti.
Uzavřeme tuto závorku a vraťme se k našim výsledkům:86 odpovídajících řádků. 20 odpovídajících souborů. Jak jsou však distribuovány odpovídající řádky v odpovídajících souborech ? Můžeme to vědět pomocí -c
možnost grep
to bude počítat počet odpovídajících řádků na zkoumaný soubor (včetně souborů s nulovou shodou):
[email protected]:~$ grep -irwc --include='*.js' Opal .
...
Tento výstup často potřebuje nějaké následné zpracování, protože zobrazuje své výsledky v pořadí, v jakém byly soubory prozkoumány, a také obsahuje soubory bez jakékoli shody – něco, co nás obvykle nezajímá. To poslední je docela snadné vyřešit:
[email protected]:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$'
Co se týče řazení věcí, můžete přidat příkaz sort na konec kanálu:
[email protected]:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$' | sort -t: -k2n
Nechal jsem vás zkontrolovat sort
příkazový manuál pro přesný význam možností, které jsem použil. Nezapomeňte se podělit o své poznatky pomocí sekce komentářů níže!
10. Nalezení rozdílu mezi dvěma odpovídajícími množinami
Pokud si vzpomínáte, před několika příkazy jsem hledal slovo "Opál." Pokud však hledám ve stejné sadě souborů všechny výskyty řetězce "Opál," dostávám dalších asi dvacet odpovědí:
[email protected]:~$ grep -irw --include='*.js' Opal . | wc -l
86
[email protected]:~$ grep -ir --include='*.js' Opal . | wc -l
105
Bylo by zajímavé najít rozdíl mezi těmito dvěma sadami. Jaké jsou tedy řádky obsahující čtyři písmena „opál“ v řadě, ale kde tato čtyři písmena netvoří celé slovo?
Na tuto otázku není tak snadné odpovědět. Protože stejné řádek může obsahovat obě slovo Opal stejně jako nějaké větší slovo obsahující tato čtyři písmena. Ale jako první přiblížení můžete použít tento kanál:
[email protected]:~$ grep -ir --include='*.js' Opal . | grep -ivw Opal
./npm/examples.js: const opalBuilder = OpalBuilder.create();
./npm/examples.js: opalBuilder.appendPaths('build/asciidoctor/lib');
./npm/examples.js: opalBuilder.appendPaths('lib');
...
Moje další zastávka by zřejmě byla prozkoumat opalBuilder
objekt, ale to bude na jiný den.
Poslední slovo
Samozřejmě nepochopíte organizaci projektu, tím méně architekturu kódu, když vydáte pár grep
příkazy!
Považuji však tento příkaz za nevyhnutelný pro identifikaci referenčních hodnot a výchozích bodů při zkoumání nové kódové základny.
Doufám tedy, že vám tento článek pomohl pochopit sílu grep
příkaz a že si jej přidáte do truhly s nástroji. Bezpochyby toho nebudete litovat!