DaleSchool

검색과 필터

입문25분

학습 목표

  • grep으로 파일에서 원하는 텍스트를 찾을 수 있다
  • 파이프(|)로 명령어를 연결해 복잡한 작업을 처리할 수 있다
  • find로 파일 시스템에서 조건에 맞는 파일을 검색할 수 있다

동작하는 코드

예제 1: grep으로 텍스트 검색

먼저 검색할 파일을 준비합니다:

cat Documents/notes.md

출력:

# 메모
- 항목 1
- 항목 2

특정 단어가 포함된 줄 찾기:

grep "항목" Documents/notes.md

출력:

- 항목 1
- 항목 2

줄 번호도 함께 표시:

grep -n "항목" Documents/notes.md

출력:

2:- 항목 1
3:- 항목 2

예제 2: 대소문자 무시 검색

grep -i "hello" Documents/hello.txt

출력:

Hello, World!

-i 옵션은 대소문자를 구분하지 않습니다. Hello, hello, HELLO 모두 검색됩니다.

예제 3: 파이프로 명령어 연결

# ls 출력에서 특정 패턴 필터링
ls | grep ".md"

출력:

notes.md
# 파일 목록의 개수 세기
ls | wc -l

출력:

       4
# 3단계 파이프: ls → grep → wc
ls | grep ".txt" | wc -l

출력:

       1

직접 수정하기

grep 다양한 옵션

# 여러 파일에서 검색
grep "Hello" Documents/hello.txt Documents/notes.md

# 하위 디렉토리까지 재귀 검색
grep -r "항목" Documents/

# 파일 이름만 출력 (내용 말고)
grep -rl "Hello" .

# 매칭 횟수만 출력
grep -c "항목" Documents/notes.md

출력 예시:

2
# 매칭되지 않는 줄 출력 (역방향 검색)
grep -v "항목" Documents/notes.md

출력:

# 메모

find로 파일 찾기

# 이름으로 파일 찾기
find . -name "*.txt"

출력 예시:

./Documents/hello.txt
./Downloads/readme.txt
# 특정 타입만 (f=파일, d=디렉토리)
find . -type f -name "*.md"

# 최근 1일 이내 수정된 파일
find . -mtime -1

# 특정 크기 이상
find . -size +1k
# 찾은 파일에 명령어 실행 (-exec)
find . -name "*.txt" -exec cat {} \;

"왜?" — 파이프는 왜 강력한가?

파이프(|)는 한 명령어의 stdout을 다음 명령어의 stdin으로 연결합니다.

ls -la | grep ".txt" | wc -l
   ↓          ↓           ↓
파일 목록  .txt 필터   줄 수 세기

단계별로 보면:

  1. ls -la → 파일 목록 출력
  2. grep ".txt".txt가 포함된 줄만 남김
  3. wc -l → 남은 줄 수 세기

결과: 현재 디렉토리의 .txt 파일 개수

파이프의 핵심 원칙: 단순한 도구들을 조합해 복잡한 작업을 처리합니다. 각 명령어는 한 가지 일만 잘 하고, 파이프로 연결해서 복잡한 문제를 해결합니다.

grep 주요 옵션 정리

| 옵션 | 설명 | 예시 | | ---- | ------------------ | ----------------- | | -i | 대소문자 무시 | grep -i "hello" | | -n | 줄 번호 표시 | grep -n "error" | | -r | 하위 디렉토리 포함 | grep -r "bug" . | | -l | 파일 이름만 표시 | grep -rl "todo" | | -v | 매칭 제외 | grep -v "#" | | -c | 매칭 줄 수만 | grep -c "error" |

실무에서 grep/파이프 활용

# 로그에서 에러만 찾기
cat app.log | grep "ERROR"

# 에러 라인 수 세기
cat app.log | grep "ERROR" | wc -l

# 특정 패턴 제외하고 보기
cat app.log | grep -v "DEBUG"

# 코드에서 특정 함수 찾기
grep -rn "getUserById" src/

흔한 실수

실수 1: grep 패턴에 특수 문자

# . 은 정규식에서 '임의의 문자'를 의미
grep "file.txt" .    # file.txt뿐 아니라 file_txt도 매칭됨

# 리터럴로 찾으려면 이스케이프
grep "file\.txt" .
# 또는 -F (고정 문자열)
grep -F "file.txt" .

실수 2: -r 없이 디렉토리 검색

grep "텍스트" Documents/   # 에러
# grep: Documents/: Is a directory

grep -r "텍스트" Documents/  # 올바름

실수 3: find 결과를 grep으로 전달하기

# 잘못된 방법: 파일 내용이 아닌 파일명에서 grep
find . -name "*.txt" | grep "hello"   # 파일명에 "hello"가 있는 것만 찾음

# 올바른 방법: 파일 내용 검색
grep -r "hello" --include="*.txt" .

심화 학습

정규식(regex)으로 패턴 검색

grep은 정규식을 지원합니다:

# ^ : 줄 시작
grep "^-" Documents/notes.md

# $ : 줄 끝
grep "2$" Documents/notes.md

# . : 임의의 문자 하나
grep "항.1" Documents/notes.md

# []: 문자 집합 중 하나
grep "[12]" Documents/notes.md

# 확장 정규식 (-E)
grep -E "항목 [12]" Documents/notes.md

정규식은 강력하지만 처음에는 어렵습니다. 기본 옵션만으로도 대부분의 검색이 가능하니 필요할 때 배워도 됩니다.

find의 강력한 -exec 옵션

찾은 파일에 직접 명령어를 실행합니다:

# 모든 .log 파일 삭제
find . -name "*.log" -exec rm {} \;

# 모든 .txt 파일 내용 출력
find . -name "*.txt" -exec cat {} \;

# 찾은 파일 목록을 파일로 저장 (리다이렉션 사용)
find . -name "*.md" > md-files.txt

{}는 찾은 파일 경로로 치환되고, \;는 명령어의 끝을 나타냅니다.

grep 대신 ripgrep(rg) 쓰는 이유

개발자들 사이에서는 rg(ripgrep)을 많이 씁니다:

brew install ripgrep

# 일반 사용 (grep -r과 유사)
rg "함수명" src/

# .gitignore 자동으로 무시
# 색상 출력 기본 지원
# grep보다 수십 배 빠름

대용량 코드베이스(수만 개 파일)에서 특히 효과적입니다. VS Code의 전체 검색도 내부적으로 ripgrep을 씁니다.

  1. grep "Hello" Documents/hello.txt로 파일에서 텍스트를 검색하세요.
  2. grep -n "항목" Documents/notes.md로 줄 번호와 함께 검색하세요.
  3. grep -c "항목" Documents/notes.md로 매칭된 줄 수를 세세요.
  4. ls | grep ".txt"로 현재 가상 파일시스템에서 .txt 파일만 나열하세요.
  5. grep -r "Hello" .으로 현재 디렉토리 전체에서 "Hello"를 검색하세요.

Q1. ls | grep ".txt" | wc -l 이 명령어가 출력하는 것은?

  • A) .txt 파일의 내용
  • B) 현재 디렉토리에서 이름에 ".txt"가 포함된 항목의 수
  • C) .txt 파일의 총 크기
  • D) .txt 파일의 목록