Посібник для розробника: Як шукати код за допомогою grep

Посібник для розробника: Як шукати код за допомогою grep
Переклад 11 хв. читання

На якому б етапі веб-розробки ви не знаходилися, вам доведеться шукати текст або шаблони у своєму коді. Можливо, ви захочете знайти змінну, яка викликає повідомлення про помилку, клас CSS, зображення, що використовується в HTML або джерелі розмітки, журнали вашої програми - список можна продовжувати до нескінченності.

Пошук у коді та тексті це одна з найпоширеніших задач, які ви будете виконувати під час веб-розробки. Ви можете використовувати інтегроване середовище розробки (IDE) для пошуку файлів проєкту, пошук файлів у вашій операційній системі або навіть пошук коду на GitHub чи іншому сервісі хостингу коду. Ви швидко зрозумієте, що вам потрібен ефективний інструмент, який допоможе вам виконувати різні види пошуку; саме тут і з'являється grep.

У цій статті ми розглянемо, що таке grep, що він може робити та чому я вважаю, що це один з найпотужніших інструментів командного рядка, який ви будете використовувати при роботі з кодом. Якщо ви не знайомі з grep, в цій статті ми розглянемо його основи, деякі поширені приклади, в тому числі те, як я використовую його щодня, і чому я вважаю, що це важливий інструмент для розробників. Тож почнемо розбиратися і з'ясуємо, як використовувати grep у ваших діях!

Що таке grep?

grep - це інструмент командного рядка, який дозволяє використовувати регулярні вирази для пошуку шаблонів. Базове використання виглядає наступним чином:

grep pattern file

Існують різні версії grep, з різними опціями та можливостями, але основна поведінка здебільшого однакова. Ви можете запустити grep --version, щоб перевірити, яка з версій у вас встановлена:

grep --version

#grep (GNU grep) 3.7
#Copyright (C) 2021 Free Software Foundation, Inc.

Порада: якщо ви застрягли або вам потрібно нагадати, як працює grep, скористайтеся командою man ("посібник", англ. manual), щоб переглянути документацію і з'ясувати, які опції можна використовувати:

man grep

Початок роботи з grep

Розглянемо grep на практичних прикладах, використовуючи репозиторій GitHub mdn/content. Щоб виконати приклади в цій статті, клонуйте репозиторій mdn/content і перейдіть в цей каталог (ви також можете завантажити репозиторій у вигляді zip-файлу з GitHub, якщо ви не використовуєте git):

git clone https://github.com/mdn/content.git
cd content

Після того, як ви перейшли до репозиторію вмісту у вашому командному рядку, ви можете виконати пошук за деякими ключовими словами та поглянути на результати grep. Наприклад, тут я шукаю слово "Communication" у файлі CONTRIBUTING.md, слово "node" у файлі package.json і фразу "Mozilla Community" у файлі CODE_OF_CONDUCT.md.

grep "Communication" CONTRIBUTING.md
# [get in touch with us]: https://developer.mozilla.org/en-US/docs/MDN/Community/Communication_channels
grep "node" package.json
#    "node": ">=18.0.0"
grep "Mozilla Community" CODE_OF_CONDUCT.md
# [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).

Як ви можете бачити, кожен з результатів grep - це рядок або список рядків, які відповідають шуканому слову або фразі. Хоча ви можете запустити команду типу grep Communication CONTRIBUTING.md і отримати ті самі результати, рекомендується взяти шаблон, який ви шукаєте, у подвійні лапки, щоб уникнути проблем з пробілами або спеціальними символами, які інтерпретуються командним інтерпретатором.

Ці приклади надзвичайно корисні для перевірки таких речей, як версія node js або URL-адреса, на яку ми вказуємо спільноті, щоб вони могли зв'язатися з нами. На цьому етапі ми знаємо, як шукати певне слово або групу слів в одному файлі.

Рекурсивний пошук

Рекурсивний пошук означає пошук вашого шаблону в декількох файлах, каталогах і підкаталогах. Вам може бути потрібно шукати шаблон не лише у файлі, а й у цілому дереві. Саме тут grep стає по-справжньому корисним. Щоб виконати рекурсивний пошук, використовуйте прапорець -r перед шаблоном:

grep -r "my pattern" ./directory

У контексті пошуку в сховищі контенту ми можемо шукати ключове слово в усіх файлах розмітки:

grep -r "TOFU" ./files
# ./files/en-us/web/security/index.md:- TOFU
# ./files/en-us/glossary/tofu/index.md:title: TOFU
# ./files/en-us/glossary/tofu/index.md:slug: Glossary/TOFU

Результат показує, що сторінка web/security/index.md має посилання на статтю глосарію для TOFU, а стаття глосарію, як і очікувалося, має кілька співпадінь для шаблону. Я пропустив кілька інших збігів, щоб зробити результат більш читабельним, але суть полягає в тому, що ми можемо знайти входження шаблону в декількох файлах і дізнатися, де він використовується.

Виключення каталогів і файлів з пошуку

У ваших проєктах розробки, швидше за все, є створені каталоги, такі як node_modules або каталоги збірок, такі як dist або build, які ви хотіли б ігнорувати під час рекурсивного пошуку шаблону. Деякі плагіни оболонки grep можуть допомогти з ігноруванням каталогів контролю версій, таких як .git, але завжди корисно знати, як явно ігнорувати певні каталоги для пошуку, коли вам це потрібно.

Щоб ігнорувати каталоги, ви можете скористатися параметром --exclude-dir. У цьому прикладі я рекурсивно шукаю "cli-progress", починаючи з поточного каталогу, але виключаю з пошуку каталог node_modules. Крапка . у кінці команди - це шлях для пошуку, яким у цьому випадку є поточний каталог:

grep -r --exclude-dir="node_modules" "cli-progress" .
# ./yarn.lock:    cli-progress "^3.12.0"
# ./yarn.lock:cli-progress@^3.12.0:
# ...

Результати цієї команди grep є гарним початком, але у файлі yarn.lock є багато зайвих співпадінь, які трохи відвертають увагу. Тому проігноруймо файл yarn.lock за допомогою опції --exclude:

grep -r --exclude-dir="node_modules" --exclude="yarn.lock" "cli-progress" .
# ./package.json:    "cli-progress": "^3.12.0",
# ./scripts/front-matter_linter.js:import cliProgress from "cli-progress";

Це набагато корисніше, оскільки у мене є два відповідних збіги, і я можу відразу це побачити:

Ми використовуємо версію ^3.12.0 cli-progress Ми імпортуємо cli-progress у скрипт front-matter_linter.js як cliProgress Якби я захотів, я міг би продовжити своє дослідження, виконавши пошук cliProgress, щоб побачити, де і як він використовується в скрипті.

Виконання пошуку без урахування регістру

Під час пошуку у сховищі контенту мені часто потрібно перевірити, чи зустрічається ключове слово. Однак, оскільки я не знаю, чи є воно першим словом речення, частиною URL-адреси або відповідає іншій угоді про регістр, за яким шаблоном мені шукати? На щастя, цю ситуацію можна виправити, якщо виконати пошук без урахування регістру за допомогою прапорця -i у grep:

grep -ri "github actions" ./files
# ./files/en-us/mdn/community/contributing/our_repositories/index.md:  A growing collection of reusable GitHub Actions for use on MDN Web Docs repositories.
# ...

Ігнорування бінарних файлів

Якщо ви шукаєте певний рядок, а в проєкті є бінарні файли, ви можете зіткнутися з несподіваними збігами:

grep -ri "linux" ./files/en-us/web/http
# oh no:
# Binary file ./files/en-us/web/http/content_negotiation/httpnegotiation.png matches

Це може статися випадково, якщо ви шукаєте короткий (два або три символи) рядок, а є багато двійкових файлів, наприклад, зображень, PDF-файлів або інших мультимедійних файлів. На щастя, ви можете ігнорувати двійкові файли за допомогою параметра --binary-files:

grep -ri --binary-files=without-match "linux" ./files/en-us/web/http

Ви також можете використовувати прапорець -I, щоб ігнорувати двійкові файли, але ви самі вирішуєте, чи хочете ви вказувати ці опції. Наведені нижче прапори виглядають для вас зрозумілими чи ви віддаєте перевагу явному опису?

grep -riI "linux" ./files/en-us/web/http

Використання регулярних виразів

Звичайно, регулярні вирази лежать в основі grep, тому розглянемо, як можна використовувати їх для пошуку варіацій шаблону. Скажімо, мені цікаво, чи є у нас сторінки, заголовки яких починаються з чисел, але я не впевнений, що це за числа. Для цього скористаймося екрануванням класу символів \d:

grep -r "title: \d\d\d" ./files/
# ./files//en-us/web/http/status/307/index.md:title: 307 Temporary Redirect
# ./files//en-us/web/http/status/300/index.md:title: 300 Multiple Choices
# ...

Тепер я знаю, що у нас є велика кількість сторінок, які використовують цей шаблон для кодів стану HTTP. Далі мені потрібно знайти сторінки, які використовують застарілі макроси, зокрема макроси {{SpecName}} і {{spec2}}.

Я хочу використати regex на кшталт SpecName|spec2, який буде відповідати або SpecName, або spec2 за допомогою диз'юнкції. У моїй версії grep за замовчуванням цю можливість не ввімкнено, тому мені потрібно увімкнути розширені регулярні вирази за допомогою прапорця -E:

grep -riE "SpecName|spec2" ./files
# files/en-us/mdn/writing_guidelines/howto/json_structured_data/index.md:The `{{SpecName}}` and `{{Spec2}}` macros ...

Чудово, єдине місце, де ми знайшли макроси, - це наші інструкції з написання текстів, які описують, як їх замінити. Ми навчилися використовувати складніші регулярні вирази та створили потужну команду пошуку, що поєднує ці опції:

  • -r для рекурсивного пошуку
  • -i для пошуку без урахування регістру
  • -E для включення розширених регулярних виразів.

Використання каналів unix з grep

У середовищі unix існує загальна домовленість про програми, які дуже добре виконують одну задачу. Це дозволяє нам створити програму, яка є конвеєром команд, що спрямовує вихідні дані однієї команди як вхідні дані для іншої. Ви можете бачити, що це називається "міжпроцесна комунікація", але ідея полягає в тому, що ви використовуєте символ |, щоб об'єднати команди в конвеєр або ланцюжок.

command1 | command2

В інтересах вивчення каналів, чому б не використати grep, щоб дізнатися, як часто я використовую grep? Скористаймося трьома командами разом, щоб дізнатися, скільки разів я використовував grep у своїй історії командного інтерпретатора:

history | grep "grep" | wc -l
# 1164

Щоб зрозуміти, що відбувається, давайте покроково розглянемо вивід кожної команди. Команда history виводить список всіх команд, які я виконував:

history
#    1   ls
#    2   mkdir ~/Code
#    ... 10000+ lines later
# 10098  man grep

Використовуючи grep "grep", виведіть лише ті рядки, які містять рядок "grep":

history | grep "grep"
#    91  grep -r "prettier"
#    92  grep -r "inline-size" .
#    ...
# 10098  man grep

Щоб отримати повну версію команди, ми додаємо команду wc -l (word count, lines) для підрахунку кількості рядків у виведенні:

history | grep "grep" | wc -l
# 1164, magic ✨

Ми написали невелику програму, яка виконує наступні дії, використовуючи канали unix:

  • history: Виводить історію запущених команд
  • grep "grep": Шукає рядок "grep" у виведенні команди history
  • wc -l: Підраховує кількість рядків у виведенні команди grep "grep"

Наскільки швидко працює grep?

Я шукаю в mdn/content кілька разів на день входження ключових слів, API, назв документів або використання певної функції у фрагментах коду. Щоб знайти шаблон за допомогою grep у приблизно 13 500 файлах, потрібно близько 0,7 секунди:

time grep -r "\`\`\`plain" files/en-us/web/ | wc -l
     252
grep -r  0.43s user 0.29s system 99% cpu 0.730 total
wc -l  0.00s user 0.00s system 0% cpu 0.729 total

Звичайно, існують швидші альтернативи, такі як ripgrep і ag, але grep працює досить швидко для більшості випадків використання. У наступному розділі я поясню, чому я вважаю, що варто вивчити grep, а не ці альтернативи.

Навіщо вивчати grep?

Якщо ця стаття все ще не переконала вас, що grep - найкращий інструмент для пошуку коду, на вивчення якого ви можете витратити свій час, ось чому я вважаю, що це того варте:

  • grep може впоратися з пошуком великої кількості тексту досить швидко.
  • Він є скрізь. Ймовірно, він доступний за замовчуванням на серверах або машинах, до яких ви підключаєтеся по SSH або входите в систему.
  • Історія вашого командного рядка дозволяє повторно використовувати попередні команди або використовувати їх як відправну точку.
  • Початківці можуть почати з простих випадків використання, а складніші шаблони можна вивчити з часом.
  • Ви можете спрямовувати вивід інших програм у grep і створювати потужні конвеєри.
  • Ви вивчите регулярні вирази, які стануть у пригоді в мовах програмування та інших інструментах.

Висновок

grep настільки корисний для мене, що я вже звик набирати grep -r, щоб підготувати пошук за заданим шаблоном, як м'язову пам'ять. Я думаю, що вивчення grep буде одним з найкращих кроків, які ви можете зробити для підвищення продуктивності під час написання коду, налагодження, перевірки нових проєктів або швидкого аналізу проєкту.

Джерело: Developer essentials: How to search code using grep
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація