Step 20. 검색 (grep/find) — 건초더미에서 바늘 두 개 찾기

Step 20. 검색 (grep/find) — 건초더미에서 바늘 두 개 찾기

Level 0 — 컴퓨터 조작과 구조의 이해 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간

전제: Step 19 완료. 가상머신 속 우분투 터미널에서 진행합니다.

  • 준비물: 우분투 가상머신, 터미널. Step 18~19의 명령들이 오늘 전부 재료로 쓰입니다.
  • 주의: 오늘 실습은 읽기와 검색만이라 100% 안전합니다. 결과가 많이 나오는 챕터인데 겁내지 마세요 — 많이 나오는 법을 배운 뒤에 줄이는 법도 배웁니다.
  • 실측 안내: 이 챕터의 출력은 Ubuntu 24.04에서 실제 실행한 결과입니다. 실측은 관리자(root) 계정으로 진행되어 경로에 /root가 보이고, 권한 오류 실험(3-2절)은 일반 계정의 처지를 재현하기 위해 권한 없는 계정으로 따로 실행해 확인했습니다. 여러분의 VM에서는 홈이 /home/사용자이름으로 보입니다.

수사관이 사건 현장에서 던지는 질문은 두 종류입니다: "이름이 ○○인 사람을 찾아라"(신원으로 찾기)와 "이런 말을 한 사람을 찾아라"(내용으로 찾기). 파일 시스템도 같습니다. 오늘의 두 주인공 — grep은 내용으로, find는 이름·조건으로 찾습니다. Step 4에서 Select-String으로 로그를 뒤졌던 것, 기억하시죠? grep은 그것의 리눅스 원조이자, 전 세계 보안 분석가가 매일 수백 번 치는 명령입니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • grep(내용 검색)과 find(이름·조건 검색)의 역할 차이를 설명한다
  • grep -r, -i, -n 옵션으로 폴더째 내용 검색을 한다
  • find로 이름·크기·수정 시간 조건 검색을 한다
  • 2>/dev/null의 각 부분을 수도관 지식으로 해독하고 쓸 수 있다
  • find와 grep을 파이프로 연결해 "좁히고, 또 좁히는" 수사를 한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 우분투 리눅스 터미널 (bash)
오늘의 명령어 grep -r/-i/-n(내용 검색), find -name/-size/-mtime(조건 검색), 2>/dev/null(오류 버리기), history | grep(과거 명령 찾기)
필요한 개념 와일드카드(*), 표준 스트림 번호(Step 19), /dev/null, 파이프 연쇄

2-1. grep — 문서 안의 돋보기

grep의 기본형: grep "찾을말" 파일. 그 파일에서 "찾을말"이 포함된 을 보여 줍니다.

핵심 옵션 세 개:

옵션 의미
-r 폴더째로 뒤진다 (재귀 — 하위 폴더 전부)
-i 대소문자 무시 (Password도 PASSWORD도)
-n 줄 번호도 함께 표시

이름의 유래가 재미있습니다 — 유닉스의 고대 텍스트 에디터 명령 "globally search a regular expression and print"(정규식으로 전체를 검색해 출력해라)의 앞글자입니다. 정규식은 Level 1에서 정식으로 배우고, 오늘은 "글자 그대로 검색"만으로도 충분히 강력합니다.

2-2. find — 조건 수사관

find의 기본형: find 어디서 -조건 값. 예시로 감을 잡으세요:

명령 의미
find ~ -name "*.txt" 내 집 아래서 이름이 .txt로 끝나는 것
find ~ -size +10M 10MB보다 큰 것 (+는 "초과", -는 "미만")
find ~ -mtime -1 최근 하루(-1) 안에 수정된 것

보안 관점에서 find의 묘미: "최근에 바뀐 파일" 검색은 침해 조사의 고전입니다 — 공격 직후에 시스템의 어느 파일이 건드려졌는지 찾는 기술. "수상하게 큰 파일" 찾기는 데이터 유출 흔적을 찾는 기술입니다.

2-3. 2>/dev/null 해독 — 오늘의 마법 주문

시스템 전체를 검색하면 읽을 권한이 없는 폴드에서 "Permission denied(권한 거부)" 오류가 무더기로 뿜어져 나와 결과가 오류에 묻힙니다. 그때 쓰는 관용구가 2>/dev/null입니다. Step 19의 지식으로 해독해 봅시다:

  • 2 — 오류 수도관 (stderr의 번호)
  • > — 방향 바꾸기 (리다이렉션)
  • /dev/null — 리눅스의 쓰레기통 파일 (들어간 것은 전부 사라짐)

합치면: "오류는 쓰레기통으로 보내고, 정상 결과만 보여 줘." /dev/null은 "모든 것은 파일" 철학의 재미있는 실례이기도 합니다 — 쓰레기통조차 파일입니다.

2-4. 언제 어느 쪽을 쓰는가

상황 도구
파일이름을 알거나 일부 안다 find
파일내용의 일부를 안다 grep
시간·크기 같은 속성으로 찾는다 find
로그에서 특정 단어가 있는 이 궁금하다 grep

실전에서는 둘을 연결합니다: find로 후보 파일을 좁히고, 그 안에서 grep으로 내용을 찾는 식입니다.


3. 따라 하기

3-1. grep 기본 — 한 파일에서 찾기

grep "root" /etc/passwd
root:x:0:0:root:/root:/bin/bash

(2026-09-09 실측.)

출력 읽는 법: /etc/passwd에서 "root"가 포함된 줄입니다. 계정 파일이니 root 줄이 나오는 게 당연하죠. 이번엔 대소문자 실험:

grep -i "ROOT" /etc/passwd
root:x:0:0:root:/root:/bin/bash

(2026-09-09 실측.)

같은 결과가 나옵니다. -i가 대소문자를 무시했기 때문입니다. 검색할 때 대소문자를 모르면 -i를 붙이는 게 안전합니다.

3-2. grep -r — 폴더째 뒤지기 (그리고 권한 오류)

grep -r "password" /etc 2>/dev/null | head -6
/etc/default/useradd:# The number of days after a password expires until the account
/etc/ssl/openssl.cnf:# input_password = secret
/etc/ssl/openssl.cnf:# output_password = secret
/etc/ssl/openssl.cnf:challengePassword		= A challenge password
/etc/pam.d/su:# This allows root to su without passwords (normal operation)
/etc/pam.d/su:# su without a password.

(2026-09-09 실측. 환경에 따라 결과가 다릅니다.)

출력 읽는 법: 파일이름:내용 형식 — 어느 파일의 어느 줄인지가 앞에 붙습니다. /etc 전체를 뒤져서 "password"를 포함한 줄을 찾은 겁니다.

그런데 2>/dev/null빼고 일반 사용자로 실행하면 어떻게 될까요? 실측해 봤습니다 (권한 없는 계정으로 실행):

grep: /etc/shadow-: Permission denied
grep: /etc/sudoers: Permission denied
grep: /etc/gshadow: Permission denied
grep: /etc/landscape/client.conf: Permission denied

(2026-09-09 실측 — root가 아닌 계정으로 grep -r "password" /etc를 실행했을 때의 실제 오류 메시지입니다. 여러분의 VM 계정으로 그냥 실행하면 이 광경을 직접 볼 수 있습니다.)

2>/dev/null 덕분에 3-2절 본문에서는 이 오류들이 한 줄도 안 나온 겁니다. 빼고 실행 → 오류 무더기, 붙이고 실행 → 결과만. 이 대비가 확실해야 습관이 됩니다.

왜 하는가: 이 한 줄이 "설정 파일 어딘가에 있는 비밀번호 관련 설정 찾기"입니다. 침해 조사에서 "공격자가 남긴 문자열을 전체에서 검색"할 때도 정확히 이 형태를 씁니다.

3-3. find 기본 — 이름으로 찾기

find /etc -name "*.conf" 2>/dev/null | head
/etc/pam.conf
/etc/modules-load.d/modules.conf
/etc/PackageKit/PackageKit.conf
/etc/PackageKit/Vendor.conf
/etc/landscape/client.conf
/etc/rsyslog.conf

(2026-09-09 실측. head는 기본 10줄 — 실측에서는 6줄만 나온 뒤 목록이 다했거나, 여러분 환경에서는 더 많이 나올 수 있습니다.)

읽는 법: /etc 아래에서 이름이 .conf로 끝나는 파일들. *.conf*는 "아무 글자나"라는 와일드카드(wildcard)입니다(Step 2에서 만난 그것 — 리눅스에도 있습니다). 뒤의 | head는 "앞부분만" — 결과가 많을 때 슬쩍 보는 Step 19의 기술입니다.

.conf 파일이 뭔가요: configuration(설정)의 줄임말 — 리눅스 설정 파일은 대부분 이 확장자를 달고 /etc에 모여 있습니다. 레지스트리(Step 11)라는 전용 DB를 쓰는 윈도우와 달리, 리눅스 설정은 찾고, 읽고, 백업하기 쉬운 텍스트입니다.

3-4. find 속성 검색 — 크기와 시간

find ~ -mtime -1 2>/dev/null | head
/root
/root/stream-lab
/root/stream-lab/filelist.txt
/root/stream-lab/a.txt
/root/stream-lab/live.log
/root/practice
/root/practice/file2.txt
...

(2026-09-09 실측 — Step 18~19에서 만든 파일들이 줄줄이 나옵니다. 여러분의 결과도 오늘 만든 것들로 채워질 겁니다.)

최근 하루 안에 수정된 파일들입니다. 크기 조건도 해 보세요:

find ~ -size +1M 2>/dev/null

내 홈에서 1MB보다 큰 파일들. (연습용 VM이라 없을 수도 있습니다 — 없으면 -size +100k로 기준을 낮춰 보세요. 실측 환경에서는 캐시 파일 두 개가 걸렸습니다.)

조사관의 눈으로 읽기: "오늘 하루 동안 이 컴퓨터에서 바뀐 파일 목록" — 이게 타임라인 조사의 리눅스 버전입니다. Step 12의 이벤트 로그 타임라인과 같은 사고방식, 다른 도구입니다.

3-5. history 검색 — 내 과거에서 찾기

history | grep find
    1  find /etc -name "*.conf" 2>/dev/null | head
    3  find ~ -mtime -1

(2026-09-09 실측 — find 명령들만 골라낸 모습입니다. 여러분의 출력은 오늘 친 순서와 번호가 그대로 찍힙니다.)

읽는 법: Step 18에서 본 명령 기록(history)을 파이프로 grep에 흘려서, "find를 썼던 명령들"만 골라낸 겁니다. "그 명령 뭐였더라?"를 찾는 실전 최고 팁입니다. 긴 명령을 또 치지 말고 이렇게 찾으세요.

예측해 보기: grep -r "root" /etc 2>/dev/null | wc -l은 무엇을 출력할까요? (정답: "root"가 포함된 줄의 개수. 실측 환경에서는 272가 나왔습니다. "몇 군데에 있는가"를 세는 이 패턴은 로그에서 이벤트 건수를 재는 표준 기술입니다. 직접 확인해 보세요 — 여러분 환경의 숫자는 다릅니다.)

3-6. grep에도 줄 번호를 — -n 옵션

검색 결과가 길 때 "몇 번째 줄인지"가 중요해집니다:

grep -n "root" /etc/passwd
1:root:x:0:0:root:/root:/bin/bash

(2026-09-09 실측.)

읽는 법: 줄 맨 앞의 숫자가 줄 번호입니다. "1번째 줄에서 발견"이라는 좌표가 붙었습니다. 나중에 "이 파일의 25번째 줄을 고쳐라" 같은 지시를 받거나, 로그에서 사건의 위치를 특정할 때 이 번호가 좌표가 됩니다. 습관처럼 붙여 두면 손해 볼 것 없는 옵션입니다.

3-7. 두 도구의 협업 — find 후 grep

실전 패턴 하나를 경험해 봅시다. "최근 하루 안에 바뀐 파일들 중에서 이름에 log가 들어가는 것":

find ~ -mtime -1 2>/dev/null | grep log
/root/stream-lab/live.log
/root/test-note.log

(2026-09-09 실측 — 실측 환경에서는 Step 19의 live.log와 실험용 test-note.log가 걸렸습니다. 여러분 환경에서는 Step 19에서 만든 live.log가 보일 겁니다.)

구조 읽기: find가 시간으로 1차 수사 → grep이 이름으로 2차 수사. 도구 두 개가 파이프로 연결되어 수사를 이어갑니다. 이 "좁히고, 또 좁히기"가 검색의 기본 전술입니다.


4. 미션과 연습문제

미션 — 흔적 찾기 게임

준비물부터 만듭니다:

mkdir -p ~/search-game/deep/nested
echo "이 파일에는 비밀 토큰 hunter2 가 있습니다" > ~/search-game/deep/nested/clue.txt
echo "아무것도 없는 파일" > ~/search-game/normal.txt

이제 수사관이 되세요:

  1. 임무 1: clue.txt를 이름만으로 찾으세요 (find 사용)
  2. 임무 2: "hunter2"라는 문자열이 든 파일을 내용으로 찾으세요 (grep -r 사용)
  3. 임무 3: 오늘(최근 하루) 내 홈에서 바뀐 파일을 전부 나열하세요
  4. 임무 4: history | grep grep으로 오늘 내가 grep을 몇 번 썼는지 세어 보세요 (힌트: 뒤에 | wc -l)

스스로 검증하는 법: 임무 1과 2가 같은 파일을 가리키면 정답입니다. 두 도구가 서로 다른 경로로 같은 바늘을 찾아내는 것 — 이 대조가 오늘 챕터의 요약입니다.

연습문제

문제 1. 다음 상황에 grep과 find 중 어느 쪽을 써야 할까요? ① 이름에 config가 들어가는 파일 찾기 ② "ERROR"라는 글자가 포함된 줄 찾기 ③ 최근 1일 안에 바뀐 파일 찾기

문제 2. 2>/dev/null2, >, /dev/null 세 부분으로 나눠 각각의 의미를 설명해 보세요.

문제 3. find ~ -name *.txt처럼 패턴에 따옴표를 빼면 왜 문제가 생길까요? 누가 *를 먼저 해석해 버리는 걸까요?

문제 4. 로그에서 "error"를 검색했는데 분명 있는 단어인데 결과가 0건입니다. 가장 먼저 의심할 것과 해결 옵션은 무엇일까요?


5. 모범 답안과 완료 기준

미션 모범 답안

# 임무 1 — 이름으로
find ~ -name "clue.txt" 2>/dev/null
/root/search-game/deep/nested/clue.txt
# 임무 2 — 내용으로
grep -r "hunter2" ~/search-game 2>/dev/null
/root/search-game/deep/nested/clue.txt:이 파일에는 비밀 토큰 hunter2 가 있습니다

(2026-09-09 실측 — 여러분 환경에서는 /root 자리에 /home/사용자이름이 보입니다.)

# 임무 3 — 최근 하루
find ~ -mtime -1 2>/dev/null

# 임무 4 — grep 사용 횟수
history | grep grep | wc -l

검증하는 법: ① 임무 1과 2가 같은 경로(.../deep/nested/clue.txt)를 가리키는가 — 두 도구가 서로 다른 경로로 같은 바늘을 찾았다면 완성입니다. ② 임무 3의 목록에 방금 만든 search-game 파일들이 포함되는가. ③ 임무 4의 숫자가 실제로 친 grep 횟수와 비슷한가(history에는 방금 친 검색 명령 자체도 포함됩니다).

심화로 생각해 볼 것: 공격자가 침투 후 "password"라는 단어가 든 파일을 찾을 때 쓰는 명령은 오늘 배운 grep -r 그대로입니다. 그리고 수비자의 탐지 단서는 Step 18의 .bash_history와 Step 12의 로그에 있습니다 — 공격과 수비가 같은 명령 위에서 만납니다.

연습문제 해답

문제 1 해답. ① find (이름 조건) ② grep (내용) ③ find (시간 속성). "이름·크기·시간이면 find, 내용이면 grep"이 구분의 전부입니다.

문제 2 해답. 2는 표준 오류(stderr) 수도관의 번호, >는 출력 방향을 바꾸는 리다이렉션, /dev/null은 들어간 것이 전부 사라지는 쓰레기통 파일. 합치면 "오류는 버리고 정상 결과만 보여 줘"입니다.

문제 3 해답. 셸(bash)이 find보다 먼저 *를 해석해 버립니다 — 현재 디렉토리의 파일 이름들로 치환해 버려서 find에는 의도와 다른 내용이 전달됩니다. "*.txt"처럼 따옴표로 감싸면 패턴이 find에게 그대로 배달됩니다. "패턴은 find에게 직접 전달한다" — 따옴표가 그 배달원입니다.

문제 4 해답. 대소문자 불일치를 먼저 의심합니다 — 리눅스는 Errorerror를 다르게 봅니다. grep -i로 대소문자를 무시하세요. 로그 검색에서는 대소문자가 제멋대로라서 -i가 거의 기본 옵션입니다.

완료 기준 체크리스트

  • [ ] grep과 find의 차이(내용 vs 이름·조건)를 설명할 수 있다
  • [ ] grep -r로 폴더째 내용 검색을 할 수 있다
  • [ ] -i(대소문자 무시)와 -n(줄 번호) 옵션을 쓸 수 있다
  • [ ] 2>/dev/null의 각 부분(2, >, /dev/null)을 설명할 수 있다
  • [ ] find로 이름·크기·시간 조건 검색을 할 수 있다
  • [ ] history | grep으로 과거 명령을 찾을 수 있다
  • [ ] find와 grep을 파이프로 연결해 봤다
  • [ ] 미션: 흔적 찾기 게임 4개 임무를 완수했다

6. 흔한 실수와 해결

벽 1. grep을 쳤는데 오류가 화면을 도배한다

증상: grep: /etc/shadow: Permission denied 류가 수십 줄.
원인: 권한 없는 폴드를 검색 중 — 정상 현상이고, 그래서 관용구가 있습니다 (3-2절 실측 참고).
해결: 명령 끝에 2>/dev/null을 붙이세요. 그리고 그게 왜 작동하는지 2-3절을 다시 읽어 보세요 — 이해한 도구는 잊히지 않습니다.

벽 2. find에서 -name 뒤의 패턴이 안 먹힌다

증상: find ~ -name *.txt가 이상한 결과나 오류를 냅니다.
원인: *는 따옴표로 감싸야 합니다. 안 감싸면 셸이 먼저 해석해 버려서 find에 다른 뜻으로 전달됩니다.
해결: 항상 "*.txt"처럼 따옴표로 감싸세요.

벽 3. grep 결과가 "이진 파일입니다"라고만 나온다

증상: 내용 대신 "binary file matches"류 메시지.
원인: 검색 대상에 문서가 아닌 프로그램 파일(이진 파일)이 섞여 있고, 거기서 우연히 글자가 맞은 것입니다.
해결: 지금 단계에서는 무시해도 됩니다. "텍스트가 아닌 파일에서도 글자 조각은 맞을 수 있다"는 것만 알아두세요. 이진 파일 분석은 훨씬 뒤(Level 3)의 이야기입니다.

벽 4. find가 너무 오래 걸린다

증상: find /를 했더니 한참을 안 끝납니다.
원인: 시스템 전체(수십만 파일)를 뒤지는 중 — 정상입니다.
해결: Ctrl + C로 멈추고, 검색 범위를 좁히세요. /etc, ~처럼 "있을 만한 곳"부터 보는 것이 수사의 순서입니다. 범위 좁히기가 곧 수사력입니다.

벽 5. 검색은 되는데 대소문자 때문에 못 찾았다

증상: 분명 있는 단어인데 결과 0건.
원인: 대소문자 불일치. 리눅스는 Errorerror를 다르게 봅니다.
해결: grep -i 습관화. 특히 로그 검색에서는 -i가 거의 기본 옵션입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
grep 내용으로 줄을 찾는 돋보기
find 이름·크기·시간 조건으로 파일을 찾는 수사관
와일드카드(*) "아무 글자나" — find에는 따옴표로 감싸 전달
/dev/null 리눅스의 쓰레기통 파일 — 오류를 버리는 곳
좁히기 전술 find로 1차, grep으로 2차 — 수사는 연쇄로

오늘의 명령어

명령 하는 일
grep -rin "말" 폴더 폴더째·대소문자 무시·줄 번호 포함 내용 검색
find 폴더 -name "패턴" 이름으로 찾기
find 폴더 -size +10M / -mtime -1 크기 / 최근 수정 시간으로 찾기
명령 2>/dev/null 오류는 쓰레기통으로
grep ... | wc -l 몇 건인가 세기
find ... | grep ... 1차 수사 후 2차 수사
history | grep 단어 그 명령 뭐였더라?

명령어보다 중요한 감각

침해 조사의 대부분은 결국 "찾기"입니다 — 수상한 문자열을 찾고, 최근 바뀐 파일을 찾고, 특정 시간의 기록을 찾고. 실제 분석 현장의 하루는 "의심스러운 IP를 grep → 그 IP가 한 일을 grep → 그 시간대의 다른 기록을 grep"처럼, 검색의 결과가 다음 검색의 단서가 되는 사슬입니다. 오늘 배운 것은 그 사슬의 첫 고리입니다.

두 가지를 더 기억해 두세요. 첫째, "설치된 프로그램이 어디 있는가"를 묻는 전용 명령 which python3 같은 것도 있습니다 — find보다 빠른 지름길입니다. 둘째, 실무자들은 잘 만든 검색 명령을 개인 노트에 모아 둡니다. 검색 실력은 결국 "질문을 좁히는 능력"이고, 그 추리가 정확해지려면 시스템 구조를 알아야 합니다 — 검색 실력은 곧 시스템 이해의 척도입니다.


전부 체크되면 Step 20 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.