grep 명령어 사용법: 텍스트 검색의 기본기

grep 명령어 사용법: 텍스트 검색의 기본기

코딩 에이전트를 쓰다 보면 터미널 로그에 grep이 심심찮게 지나가는 걸 보게 됩니다. 클로드 코드(Claude Code)나 Cursor 같은 도구가 코드베이스에서 함수 정의를 찾거나 특정 패턴을 추적할 때 내부적으로 grep을 돌리거든요. 예전부터 있던 명령어인데 AI 시대에 오히려 더 자주 보게 된 셈이죠.

grepGlobally search a Regular Expression and Print matching lines의 약자입니다. 이름 그대로 파일이나 입력 스트림에서 특정 패턴과 일치하는 줄을 찾아서 출력하는 도구예요. 1973년 Unix에서 태어나서 50년이 넘게 살아남은, 말 그대로 검증된 도구입니다.

이번 글에서는 grep의 기본 사용법부터 정규표현식, 재귀 검색, 다른 명령어와의 조합까지 단계별로 살펴보겠습니다.

기본 사용법

가장 기본적인 형태는 grep 패턴 파일입니다. 파일에서 패턴이 포함된 줄을 찾아서 출력합니다.

grep "title" src/content/posts/fd.md
결과
title: "fd로 파일 찾기: find보다 빠르고 편한 검색 도구"

패턴이 포함된 줄만 쏙 뽑아서 보여주니까, 파일 전체를 열어보지 않아도 원하는 정보를 빠르게 확인할 수 있어요.

여러 파일을 동시에 검색하는 것도 됩니다. Glob 패턴을 사용하면 특정 확장자의 파일만 골라서 검색할 수 있어요.

grep "tags:" src/content/posts/*.md

이렇게 하면 posts 디렉토리의 모든 마크다운 파일에서 tags:가 포함된 줄을 찾습니다. 여러 파일에서 검색하면 각 줄 앞에 파일명이 함께 표시되어서 어떤 파일에서 나온 결과인지 바로 알 수 있죠.

줄 번호와 대소문자

검색 결과에 줄 번호가 같이 나오면 파일에서 해당 위치를 바로 찾아갈 수 있어서 편합니다. -n 옵션을 붙이면 됩니다.

grep -n "title" src/content/posts/fd.md
결과
2:title: "fd로 파일 찾기: find보다 빠르고 편한 검색 도구"
50:```sh title="결과"
78:```sh title="결과"
95:```sh title="결과"
208:```text title=".fdignore"

코드 리뷰하거나 디버깅할 때 “42번째 줄에 있어요”라고 바로 말할 수 있으니 꽤 실용적이에요.

기본적으로 grep은 대소문자를 구분합니다. hello를 검색하면 HelloHELLO는 안 잡혀요. 구분 없이 검색하고 싶을 때는 -i 옵션을 씁니다.

grep -i "hello" README.md

변수명이나 함수명은 대소문자가 섞여 있는 경우가 많으니까 -i는 꽤 자주 쓰게 됩니다.

결과 필터링과 카운팅

반대로 패턴이 포함되지 않은 줄만 보고 싶을 때도 있죠. -v 옵션이 그 역할을 합니다. 설정 파일에서 주석 줄을 걸러내는 게 대표적인 사용 예입니다.

grep -v "^#" /etc/ssh/sshd_config

#으로 시작하는 줄을 전부 제외하고 실제 설정 내용만 보여줍니다.

패턴이 몇 번 나타나는지 개수만 궁금하면 -c 옵션을 씁니다.

grep -c "fd" src/content/posts/fd.md
결과
74

파일에 fd라는 문자열이 74번 등장하는군요.

여러 파일에서 검색할 때 매칭된 내용은 필요 없고 어떤 파일에 패턴이 있는지만 알고 싶다면 -l 옵션이 딱입니다.

grep -rl "import" src/components/ --include="*.astro"
결과
src/components/ArticleFooter.astro
src/components/Search.astro
src/components/Footer.astro
src/components/ArticleContent.astro
src/components/Nav.astro

-r과 조합하면 프로젝트에서 특정 모듈을 사용하는 파일 목록을 빠르게 뽑아낼 수 있어요.

줄 전체가 아니라 패턴에 일치하는 부분만 보고 싶다면 -o 옵션을 사용합니다.

grep -oE "[0-9]+\.[0-9]+\.[0-9]+" package.json

버전 번호 같은 특정 형식의 문자열을 파일에서 추출할 때 쓰면 좋습니다.

주변 맥락 보기: -A, -B, -C

검색 결과가 나왔는데 그 줄만 봐서는 맥락을 모르겠을 때가 있죠. -A(After), -B(Before), -C(Context) 옵션으로 매칭된 줄 전후의 내용을 함께 볼 수 있습니다.

grep -B 2 -A 2 "getPublishedPosts" src/utils/posts.ts
결과
 * In development (DEV mode), drafts are included for preview.
 */
export async function getPublishedPosts() {
  const posts = await getCollection("posts", ({ data }) => {
    // In development, show all posts including drafts

-B 2는 매칭된 줄 위로 2줄, -A 2는 아래로 2줄을 보여줍니다. 위아래 같은 수의 줄을 보고 싶으면 -C를 쓰면 되는데, grep -C 2grep -B 2 -A 2와 같습니다.

함수 정의를 찾고 바로 앞뒤 코드까지 확인하고 싶을 때 특히 유용해요. 코딩 에이전트들이 코드 맥락을 파악할 때 이 옵션을 적극적으로 활용합니다.

재귀 검색: -r

프로젝트 전체에서 무언가를 찾아야 할 때 -r 옵션을 쓰면 하위 디렉토리까지 재귀적으로 검색합니다.

grep -rn "function" src/utils/ --include="*.ts"
결과
src/utils/korean.ts:9:export function hasKorean(text: string): boolean {
src/utils/tags.ts:16:function toTagId(tag: string): string {
src/utils/tags.ts:23:function buildTagLookup(tagEntries: TagEntry[]): Map<string, TagEntry> {
src/utils/tags.ts:33:export function getTopTags(
src/utils/tags.ts:51:export function getGroupTagMap(
src/utils/tags.ts:76:export function expandTagsWithKorean(
src/utils/tags.ts:98:export function getTagImage(
src/utils/og.ts:12:export function toAbsoluteUrl(path: string, siteUrl = SITE_URL): string {
src/utils/og.ts:22:export function isSvgImage(src: string): boolean {
src/utils/og.ts:34:export function extractOgImage(content: string): string | null {

--include 옵션으로 특정 확장자만 검색 대상에 포함할 수 있고, 반대로 --exclude로 제외할 수도 있습니다. --exclude-dir을 쓰면 특정 디렉토리를 통째로 건너뛸 수 있어요.

# TypeScript 파일에서만 검색
grep -rn "useState" src/ --include="*.tsx"

# node_modules 제외하고 검색
grep -rn "TODO" . --exclude-dir=node_modules

# 여러 디렉토리 제외
grep -rn "config" . --exclude-dir={node_modules,dist,.git}

node_modules.git 같은 디렉토리를 제외하지 않으면 결과가 쏟아져 나오니까 --exclude-dir은 거의 필수적으로 쓰게 됩니다.

정규표현식 활용

grep의 진짜 힘은 정규표현식(Regular Expression)에 있습니다. 기본적으로 grep은 기본 정규표현식(BRE)을 지원하는데, -E 옵션을 붙이면 확장 정규표현식(ERE)을 사용할 수 있습니다. egrep이라는 별도 명령어도 있는데, grep -E와 동일합니다.

# 줄의 시작이 import 또는 export인 줄 찾기
grep -E "^(import|export)" src/utils/posts.ts
결과
import { getCollection, type CollectionEntry } from "astro:content";
import type { ImageMetadata } from "astro";
import { remark } from "remark";
import strip from "strip-markdown";
import { extractLocalImageFilename } from "./og";
import { getGroupTagMap, getTagImage } from "./tags";
export async function getPublishedPosts() {
export function getAllTags(post: { data: { tags: string[] } }): string[] {
export function getPostsByTag(
export function getTagCounts(
export function getRelatedPosts(
export interface PostSummary {
export async function sortAndMapPosts(
export function hasBodyMedia(body: string): boolean {
export async function getExcerpt(

자주 쓰는 정규표현식 패턴 몇 가지를 정리하면 이렇습니다.

# 줄의 시작(^)과 끝($)
grep "^date:" src/content/posts/fd.md    # date:로 시작하는 줄
grep "md$" file.txt                      # md로 끝나는 줄

# 문자 클래스
grep "[0-9]" file.txt                    # 숫자가 포함된 줄
grep "[A-Z]" file.txt                    # 대문자가 포함된 줄

# 반복
grep -E "o+" file.txt                    # o가 1번 이상 반복
grep -E "go{2}d" file.txt               # good (o가 정확히 2번)
grep -E "[0-9]{3}" file.txt             # 숫자 3자리 이상

# 선택 (OR)
grep -E "error|warning" log.txt          # error 또는 warning
grep -E "\.(ts|tsx|js|jsx)$" file.txt   # TypeScript/JavaScript 확장자

# 단어 단위 매칭
grep -w "port" config.txt                # port만 매칭 (import, export 제외)

-w 옵션은 정규표현식은 아니지만 알아두면 유용합니다. 단어 경계(word boundary)를 기준으로 매칭하기 때문에 port를 검색할 때 importexport에 있는 port는 잡히지 않아요.

파이프와 조합하기

grep이 진짜 빛나는 순간은 다른 명령어와 파이프(|)로 연결할 때입니다. 어떤 명령어의 출력이든 grep으로 필터링할 수 있거든요.

# 실행 중인 프로세스에서 node 관련 것만 찾기
ps aux | grep node

# 환경 변수 중 PATH 관련 것만 보기
env | grep PATH

# 최근 커밋 메시지에서 fix가 포함된 것 찾기
git log --oneline | grep -i "fix"

# 설치된 패키지 중 특정 패키지 확인
dpkg -l | grep nginx

파이프 조합은 쉘의 리다이렉션과 함께 쓰면 더 강력해집니다. 예를 들어 검색 결과를 파일로 저장하거나, 에러 출력을 분리하는 식이죠.

# 검색 결과를 파일로 저장
grep -rn "TODO" src/ > todos.txt

# 매칭된 줄 수만 파일로 저장
grep -c "error" access.log > error_count.txt

여러 grep을 연결해서 결과를 점점 좁혀가는 패턴도 자주 씁니다.

# 로그에서 오늘 날짜의 ERROR만 찾기
grep "2026-04-15" app.log | grep "ERROR"

# Python 파일 목록 중 test가 포함된 것만
find . -name "*.py" | grep test

종료 코드 활용

grep은 패턴을 찾았는지 여부에 따라 종료 코드(exit code)를 반환합니다. 매칭되면 0, 안 되면 1이에요. 이 특성을 쉘 스크립트에서 조건문과 함께 활용할 수 있습니다.

# 파일에 특정 패턴이 있는지 확인
if grep -q "published: false" post.md; then
  echo "아직 초안입니다"
fi

-q(quiet) 옵션은 출력을 하지 않고 종료 코드만 반환합니다. 스크립트에서 패턴 존재 여부만 확인할 때 딱이에요. CI/CD 파이프라인에서 코드에 console.log가 남아 있는지 체크하거나, 설정 파일에 필수 값이 있는지 검증하는 데 쓸 수 있습니다.

# console.log가 남아 있으면 커밋 차단 (pre-commit hook)
if grep -rq "console\.log" src/ --include="*.ts"; then
  echo "console.log를 제거해주세요"
  exit 1
fi

실전 활용 패턴

지금까지 배운 옵션을 조합하면 꽤 다양한 작업을 할 수 있는데, 개발하면서 자주 쓰게 되는 패턴을 정리해봤습니다.

# 프로젝트에서 특정 함수가 어디서 사용되는지 찾기
grep -rn "getPublishedPosts" src/ --include="*.ts" --include="*.astro"

# 설정 파일에서 주석과 빈 줄 제외하고 보기
grep -vE "^(#|$)" /etc/nginx/nginx.conf

# 로그 파일에서 특정 시간대의 에러 찾기
grep "^2026-04-15T1[0-2]:" app.log | grep -i "error"

# 마크다운 파일에서 깨진 링크 후보 찾기
grep -rn "\[.*\]()" src/content/posts/

# 특정 환경 변수가 설정된 파일 찾기
grep -rl "DATABASE_URL" . --include="*.env*"

# Git 충돌 마커가 남아 있는 파일 찾기
grep -rn "^<<<<<<< " src/

# HTML에서 alt 속성이 빈 이미지 태그 찾기
grep -rn 'alt=""' src/ --include="*.html" --include="*.astro"

ripgrep: 더 빠른 대안

grep이 느리다고 느낄 만큼 큰 코드베이스를 다루고 있다면 ripgrep(rg)을 써볼 만합니다. fd의 핵심 라이브러리를 만든 BurntSushi(Andrew Gallant)가 개발한 도구로, Rust로 작성되어 있어서 grep보다 훨씬 빠릅니다.

# grep
grep -rn "function" src/ --include="*.ts"

# ripgrep (동일한 검색)
rg -n "function" src/ -t ts

ripgrep은 기본적으로 .gitignore를 존중하고, 바이너리 파일을 건너뛰고, 결과에 색상을 입혀주니까 개발 환경에서 쓰기엔 한결 편합니다. fdfind의 현대적 대안이라면, ripgrepgrep의 현대적 대안인 셈이죠.

다만 grep은 거의 모든 Unix/Linux 시스템에 기본 설치되어 있다는 강점이 있어요. 원격 서버에 SSH로 접속했을 때 ripgrep이 없을 수는 있어도 grep이 없는 경우는 거의 없습니다. Docker 컨테이너 안에서 디버깅할 때도 마찬가지고요. 그래서 grep의 기본기를 익혀두는 건 여전히 중요합니다.

마치며

grep은 단순하지만 그래서 오히려 강합니다. 텍스트에서 패턴을 찾는다는 하나의 일을 아주 잘 하고, 파이프로 다른 도구와 자유롭게 연결되니까요. 코딩 에이전트가 코드베이스를 탐색할 때 grep을 즐겨 쓰는 것도 이런 이유입니다. 한 가지 일을 확실하게 하는 도구는 사람이든 AI든 손이 가게 되어 있어요.

처음에는 -rn--include 정도만 외워두고 써보세요. 그러다 정규표현식이 필요해지고, 파이프 조합이 자연스러워지면 어느새 grep이 터미널에서 가장 먼저 떠오르는 명령어가 되어 있을 겁니다.

더 자세한 옵션은 GNU grep 매뉴얼을 참고하세요.

This work is licensed under CC BY 4.0 CC BY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord