10 exemplos práticos de comandos Grep para desenvolvedores
O comando grep é usado para encontrar padrões em arquivos. Este tutorial mostra alguns dos exemplos de comandos grep mais comuns que seriam especificamente benéficos para desenvolvedores de software.
Recentemente, comecei a trabalhar com Asciidoctor.js e nos modelos Asciidoctor.js-pug e Asciidoctor-templates. projeto js.
Nem sempre é fácil ser imediatamente eficaz quando você se aprofunda pela primeira vez em uma base de código contendo vários milhares de linhas. Mas minha arma secreta para encontrar o caminho através de tantas linhas de código é a ferramenta grep
.
Vou compartilhar com vocês como usar o comando grep no Linux com exemplos.
Exemplos úteis da vida real dos comandos grep no Linux
Se você olhar no man
, verá aquela breve descrição da ferramenta grep
: “imprimir linhas que correspondem a um padrão. ”
Contudo, não se deixe enganar por essa definição humilde: grep
é uma das ferramentas mais úteis na caixa de ferramentas Unix e há inúmeras ocasiões para usá-la assim que você trabalha com arquivos de texto.
É sempre melhor ter exemplos do mundo real para aprender como as coisas funcionam. Portanto, usarei a árvore de origem do Asciidoctor.js para ilustrar alguns dos recursos do grep
.
Você pode baixar essa árvore de origem do GitHub e, se quiser, pode até verificar o mesmo conjunto de alterações que usei ao escrever este artigo. Isso garantirá que você obtenha resultados perfeitamente idênticos aos descritos no restante deste artigo:
git clone https://github.com/asciidoctor/asciidoctor.js
cd asciidoctor.js
git checkout v1.5.6-rc.1
1. Encontre todas as ocorrências de uma string (uso básico)
Asciidoctor.js oferece suporte ao mecanismo Nashorn JavaScript para a plataforma Java. Não conheço Nashorn, então poderia aproveitar a oportunidade para aprender mais sobre ele explorando as partes do projeto que fazem referência a esse mecanismo JavaScript.
Como ponto de partida, verifiquei se havia algumas configurações relacionadas ao Nashorn no arquivo package.json
descrevendo as dependências do projeto:
linux@handbook:~$ 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",
Sim, aparentemente houve alguns testes específicos de Nashorn. Então, vamos investigar isso um pouco mais.
2. Pesquisa sem distinção entre maiúsculas e minúsculas em um conjunto de arquivos
Agora, quero dar uma olhada mais de perto nos arquivos do diretório ./npm/test/
que mencionam explicitamente Nashorn.
Uma pesquisa sem distinção entre maiúsculas e minúsculas (opção -i
) é provavelmente melhor aqui, pois preciso encontrar ambas as referências a nashorn
e Nashorn
(ou qualquer outro combinação de caracteres maiúsculos e minúsculos):
linux@handbook:~$ 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');
Na verdade, a insensibilidade a maiúsculas e minúsculas foi útil aqui. Caso contrário, eu teria perdido a instrução require('../module/nashorn')
. Sem dúvida, devo examinar esse arquivo com mais detalhes posteriormente.
3. Encontre todos os arquivos não correspondentes
A propósito, existem alguns arquivos não específicos do Nashorm no diretório npm/test/
? Para responder a essa pergunta, podemos usar a opção “imprimir arquivos não correspondentes” do grep (opção -L
):
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
Observe como com a opção -L
a saída de grep
mudou para exibir apenas nomes de arquivos. Portanto, nenhum dos arquivos acima contém a string “nashorn” (independentemente do caso). Isso não significa que não estejam de alguma forma relacionados a essa tecnologia, mas pelo menos as letras “n-a-s-h-o-r-n” não estão presentes.
4. Encontrar padrões em arquivos ocultos e recursivamente em subdiretórios
Os dois últimos comandos usaram um padrão shell glob para passar a lista de arquivos a serem examinados para o comando grep
.
No entanto, isso tem algumas limitações inerentes: o asterisco (*
) não corresponderá aos arquivos ocultos. Nem corresponderá aos arquivos (eventualmente) contidos em subdiretórios.
Uma solução seria combinar grep
com o comando find em vez de depender de um padrão shell glob:
# This is not efficient as it will spawn a new grep process for each file
linux@handbook:~$ find npm/test/ -type f -exec grep -iL nashorn \{} \;
# This may have issues with filenames containing space-like characters
linux@handbook:~$ grep -iL nashorn $(find npm/test/ -type f)
Como mencionei nos comentários do bloco de código acima, cada solução tem desvantagens.
Em relação aos nomes de arquivos contendo caracteres semelhantes a espaços, deixo você investigar a opção grep -z
que, combinada com a opção -print0
do comando find
, pode mitigar esse problema. Não hesite em usar a seção de comentários no final deste artigo para compartilhar suas ideias sobre esse assunto!
No entanto, uma solução melhor seria usar a opção “recursiva” do grep. Com essa opção, você fornece na linha de comando a raiz da sua árvore de pesquisa (o diretório inicial) em vez da lista explícita de nomes de arquivos a serem examinados.
Com a opção -r
, grep irá pesquisar todos os arquivos no diretório especificado, incluindo os ocultos, e então descerá recursivamente para qualquer subdiretório:
linux@handbook:~$ 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
Na verdade, com essa opção, eu também poderia começar minha exploração um nível acima para ver se há testes não-npm direcionados a Nashorn também:
linux@handbook:~$ grep -irL nashorn npm/
Eu deixei você testar esse comando sozinho para ver o resultado; mas como dica, posso dizer que você deverá encontrar muitos mais arquivos correspondentes!
5. Filtrando arquivos por nome (usando expressões regulares)
Portanto, parece haver alguns testes específicos de Nashorn nesse projeto. Como Nashorn é Java, outra questão que poderia ser levantada seria “existe algum arquivo fonte Java no projeto mencionando explicitamente Nashorn? ”.
Dependendo da versão do grep
que você usa, existem pelo menos duas soluções para responder a essa pergunta.
A primeira é usar grep
para encontrar todos os arquivos contendo o padrão “nashorn” e, em seguida, canalizar a saída desse primeiro comando para uma segunda instância grep
filtrando não-java Arquivos Fonte:
linux@handbook:~$ 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"));
A primeira metade do comando já deve estar compreensível. Mas e aquela parte “^[\^:]*\\.java ”?
A menos que você especifique a opção -F
, grep
assume que o padrão de pesquisa é uma expressão regular. Isso significa que, além de caracteres simples que corresponderão literalmente, você terá acesso a um conjunto de metacaracteres para descrever padrões mais complexos. O padrão que usei acima corresponderá apenas a:
^
o início da linha[^:]*
seguido por uma sequência de quaisquer caracteres, exceto dois pontos\.
seguido por um ponto (o ponto tem um significado especial em regex, então tive que protegê-lo com uma barra invertida para expressar o que quero uma correspondência literal)java
e seguido pelas quatro letras “java. ”
Na prática, como grep
usará dois pontos para separar o nome do arquivo do contexto, mantenho apenas linhas com .java
na seção do nome do arquivo. Vale a pena mencionar que corresponderia também a nomes de arquivos .javascript
. Isso é algo que deixo tentar resolver sozinho, se quiser.
6. Filtrando arquivos por nome usando grep
Expressões regulares são extremamente poderosas. No entanto, nesse caso específico, parece um exagero. Sem falar na solução acima, gastamos tempo examinando todos os arquivos em busca do padrão “nashorn” – a maioria dos resultados sendo descartados na segunda etapa do pipeline.
Se você estiver usando a versão GNU do grep
, algo que é provável se você estiver usando Linux, você tem outra solução com a opção --include
. Isso instrui o grep
a pesquisar apenas em arquivos cujo nome corresponda ao padrão glob fornecido:
linux@handbook:~$ 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. Encontrando palavras
O interessante do projeto Asciidoctor.js é que ele é um projeto multilíngue. Em sua essência, o Asciidoctor é escrito em Ruby, portanto, para ser utilizável no mundo JavaScript, ele deve ser “transpilado” usando o Opal, um compilador de fonte a fonte de Ruby para JavaScript. Outra tecnologia que eu não conhecia antes.
Assim, após ter examinado as especificidades do Nashorn, atribuí a mim mesmo a tarefa de compreender melhor a API do Opal. Como primeiro passo nessa busca, pesquisei todas as menções ao objeto global Opal
nos arquivos JavaScript do projeto. Pode aparecer em afetações (Opal =
), acesso de membros (Opal.
) ou talvez até mesmo em outros contextos. Uma expressão regular resolveria o problema. No entanto, mais uma vez, grep
tem uma solução mais leve para resolver esse caso de uso comum. Usando a opção -w
, ela corresponderá apenas a palavras, ou seja, padrões precedidos e seguidos por um caractere que não seja de palavra. Um caractere que não seja uma palavra é o início da linha, o fim da linha ou qualquer caractere que não seja uma letra, nem um dígito, nem um sublinhado:
linux@handbook:~$ grep -irw --include='*.js' Opal .
...
8. colorindo a saída
Não copiei a saída do comando anterior porque há muitas correspondências. Quando a saída é densa assim, você pode adicionar um pouco de cor para facilitar a compreensão. Se isso ainda não estiver configurado por padrão em seu sistema, você pode ativar esse recurso usando a opção GNU --color
:
linux@handbook:~$ grep -irw --color=auto --include='*.js' Opal .
...
Você deverá obter o mesmo resultado longo de antes, mas desta vez a string de pesquisa deverá aparecer em cores, caso ainda não tenha sido o caso.
9. Contando linhas ou arquivos correspondentes
Mencionei duas vezes que a saída dos comandos anteriores era muito longa. Quanto tempo exatamente?
linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86
Isso significa que temos um total de 86 linhas correspondentes em todos os arquivos examinados. No entanto, quantos arquivos diferentes correspondem? Com a opção -l
você pode limitar a saída do grep
aos arquivos correspondentes em vez de exibir os < correspondenteslinhas. Portanto, essa simples mudança dirá quantos arquivos correspondem:
linux@handbook:~$ grep -irwl --include='*.js' Opal . | wc -l
20
Se isso lembra a opção -L
, não é surpresa: como é relativamente comum, letras minúsculas/maiúsculas são usadas para distinguir opções complementares. -l
exibe nomes de arquivos correspondentes. -L
exibe nomes de arquivos não correspondentes. Para outro exemplo, deixo você verificar no manual as opções -h
/-H
.
Vamos fechar esse parêntese e voltar aos nossos resultados: 86 linhas correspondentes. 20 arquivos correspondentes. Entretanto, como são distribuídas as linhas correspondentes nos arquivos correspondentes? Podemos saber isso usando a opção -c
do grep
que contará o número de linhas correspondentes por arquivo examinado (incluindo arquivos com zero correspondências):
linux@handbook:~$ grep -irwc --include='*.js' Opal .
...
Freqüentemente, essa saída precisa de algum pós-processamento, pois exibe seus resultados na ordem em que os arquivos foram examinados e também inclui arquivos sem correspondência - algo que geralmente não nos interessa. Este último é bastante fácil de resolver:
linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$'
Quanto a ordenar coisas, você pode adicionar o comando sort no final do pipeline:
linux@handbook:~$ grep -irwc --include='*.js' Opal . | grep -v ':0$' | sort -t: -k2n
Deixei você verificar o manual do comando sort
para saber o significado exato das opções que usei. Não se esqueça de compartilhar suas descobertas usando a seção de comentários abaixo!
10. Encontrando a diferença entre dois conjuntos correspondentes
Se você se lembra, alguns comandos atrás, procurei pela palavra “Opal. ”No entanto, se eu pesquisar no mesmo conjunto de arquivos todas as ocorrências da string “Opal”, obtenho cerca de mais vinte respostas:
linux@handbook:~$ grep -irw --include='*.js' Opal . | wc -l
86
linux@handbook:~$ grep -ir --include='*.js' Opal . | wc -l
105
Encontrar a diferença entre esses dois conjuntos seria interessante. Então, quais são as linhas que contêm as quatro letras “opala” seguidas, mas onde essas quatro letras não formam uma palavra inteira?
Não é tão fácil responder a essa pergunta. Porque a mesma linha pode conter ambas a palavra Opala, bem como alguma palavra maior contendo essas quatro letras. Mas, como primeira aproximação, você pode usar esse pipeline:
linux@handbook:~$ 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');
...
Aparentemente, minha próxima parada seria investigar o objeto opalBuilder
, mas isso ficará para outro dia.
A última palavra
É claro que você não entenderá a organização de um projeto, muito menos a arquitetura do código, apenas emitindo alguns comandos grep
!
No entanto, acho esse comando inevitável para identificar benchmarks e pontos de partida ao explorar uma nova base de código.
Então, espero que este artigo tenha ajudado você a entender o poder do comando grep
e que você o adicione à sua caixa de ferramentas. Sem dúvida você não vai se arrepender!