На якому б етапі веб-розробки ви не знаходилися, вам доведеться шукати текст або шаблони у своєму коді. Можливо, ви захочете знайти змінну, яка викликає повідомлення про помилку, клас 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 буде одним з найкращих кроків, які ви можете зробити для підвищення продуктивності під час написання коду, налагодження, перевірки нових проєктів або швидкого аналізу проєкту.
Ще немає коментарів