grep
명령의 출력이나 파일에서 regexp 을 이용해 매칭 되는 라인, 스트링을 찾을 때
자주 사용되는 명령이 grep 입니다.
grep 명령은 ed
라인 에디터를 만든 Ken Thompson 이 스트링 매칭 기능을 분리해서 별도의 명령으로 만든 것으로
grep 이라는 이름은 ed
명령의 g/re/p
에서 유래합니다.
( g
는 global search 를 re
는 regular expression, p
는 print 를 의미 )
이후에 extended regular expression 을 사용하는 egrep, 단순 fixed 스트링( regexp 가 아닌 )을 검색하는 fgrep 같은 명령이 추가되었지만 지금은 grep 하나의 명령에서 -E, -F 옵션을 통해 모두 제공됩니다. 하위 호환성을 위해 /bin/egrep, /bin/fgrep 명령이 아직 존재하지만 단순히 wrapper script 에 불과합니다.
grep 명령을 잘 사용하기 위해서는 먼저 regexp 에 대해서 알고 활용할 수 있어야 합니다. 여기서는 regexp 를 이용한 여러 가지 활용 방법보다는 명령에서 제공하는 전반적인 기능에 대해서 알아보겠습니다. 다음은 옵션 설정을 통해서 grep 명령에서 사용할 수 있는 regexp 종류입니다. ( 각각의 차이점에 대해서는 이곳 을 참고하세요 )
-G, --basic-regexp
basic regular expression (BRE). 기본값 입니다.
-E, --extended-regexp
extended regular expression (ERE).
-P, --perl-regexp
Perl-compatible regular expression (PCRE).
-F, --fixed-strings
fixed strings
매칭할 스트링에 regexp 문자가 포함될 경우 escape 해야 합니다
.
문자는 regexp 에서 사용되는 특수 문자로 임의의 한 문자에 해당됩니다.
따라서 .
문자 자체를 매칭하기 위해서는 escape 해야 합니다.
여러 개의 매칭 패턴을 사용
-e PATTERN, --regexp=PATTERN
-e 옵션을 사용하면 여러 개의 매칭 패턴을 적용할 수 있습니다.
$ grep -e 'harry' -e 'pulse' /etc/passwd
pulse:x:116:124:PulseAudio daemon,,,:/var/run/pulse:/bin/false
harry:x:1000:1000:NetworkManager:/home/harry:/bin/bash
또한 검색할 스트링이 -
문자로 시작할 경우 grep 명령의 옵션과 구분하기 위해서 사용할 수 있습니다.
# 검색할 스트링 '--width' 가 grep 명령의 옵션으로 인식돼 오류 발생
$ grep '--width' *.cpp
grep: unrecognized option '--width'
Usage: grep [OPTION]... PATTERN [FILE]...
Try 'grep --help' for more information.
$ grep -e '--width' *.cpp
OK
$ grep -- '--width' *.cpp
OK
대, 소문자 구분없이 매칭
-i, --ignore-case
대, 소문자 구분 없이 매칭하려면 이 옵션을 사용하면 됩니다.
invert 매칭
-v, --invert-match
invert 매칭은 반대로 매칭되지 않는 라인을 프린트 합니다.
Context line control
grep 명령을 사용하다 보면 매칭 되는 라인을 표시하는 것 외에 더해서 이전, 이후 n 라인을 표시하는 것이 필요할 때가 있습니다. 이때 사용할 수 있는 옵션입니다.
-A NUM, --after-context=NUM
-B NUM, --before-context=NUM
-C NUM, -NUM, --context=NUM
$ cat sample.txt
aaa
bbb
ccc
ddd
eee
fff
ggg
$ grep -A 2 'ddd' sample.txt # After 2 lines
ddd # ddd
eee
fff
$ grep -B 2 'ddd' sample.txt # Before 2 lines
bbb
ccc
ddd # ddd
$ grep -C 2 'ddd' sample.txt # Context 2 lines
bbb
ccc
ddd # ddd
eee
fff
$ grep -A2 -B3 'ddd' sample.txt
...
recursive 매칭
-r, --recursive
특정 디렉토리 이하 모든 파일을 대상으로 매칭합니다.
# ./dir1 ./dir2 디렉토리 이하 모든 파일에서 'hello' 매칭
$ grep -r 'hello' ./dir1 ./dir2
# 디렉토리 인수를 주지 않으면 현재 디렉토리가 적용됩니다.
$ grep -r 'hello'
-R, --dereference-recursive
디렉토리 심볼릭 링크가 있을 경우 -r
옵션은 기본적으로 follow 하지 않는데 반해 -R
옵션은 follow 합니다.-r
옵션 사용시 특정 링크만 follow 하고 싶으면 해당 링크를 디렉토리 리스트에 같이 적어주면 됩니다.
include, exclude 설정
GLOBbing 패턴을 이용해서 특정 파일들만 포함시키거나 제외할 수 있습니다.
--include=GLOB
# 'hello' 스트링을 찾을 때 '*.c' 파일들만 검색
$ grep -r --include='*.c' 'hello'
--exclude=GLOB
# 'hello' 스트링을 찾을 때 '*.cpp' 파일을 제외하고 검색
$ grep -r --exclude='*.cpp' 'hello'
--exclude-dir=GLOB
# 'hello' 스트링을 찾을 때 '.git' 디렉토리를 제외하고 검색
$ grep -r --exclude-dir='.git' 'hello'
binary 파일은 제외하고 검색
바이너리 파일과 텍스트 파일이 혼합되어 있을 경우 이 옵션으로 바이너리 파일을 검색에서 제외할 수 있습니다.
-I
# 'hello' 스트링을 찾을 때 바이너리 파일은 제외하고 검색
$ grep -rI 'hello'
매칭 스트링만 프린트
-o, --only-matching
이 옵션은 매칭 라인을 프린트하는 것이 아니고, 매칭 되는 스트링만 프린트합니다.
$ sensors | grep -i core | grep -E -o '\+[0-9.]+'
+46.0
+86.0
+100.0
+46.0
+86.0
+100.0
+46.0
+86.0
+100.0
+46.0
+86.0
+100.0
directory, device 파일 skip 하기
-d ACTION, --directories=ACTION
-D ACTION, --devices=ACTION
입력으로 사용되는 파일명에 directory 나 device 이름이 포함될 경우 ACTION 값을 skip 으로 설정하면 skip 할 수 있습니다.
$ grep -I 'main' *
best.c:int main()
err.c:int main() {
grep: gd: Is a directory <--- directory
gd
grep: gmp: Is a directory <--- directory
gmp
main.c:int main() {
$ grep -I -d skip 'main' * # directory skip
best.c:int main()
err.c:int main() {
main.c:int main() {
n 번째 매칭까지만 프린트
-m NUM, --max-count=NUM
전체 데이터를 검색할 필요 없이 n 번째 매칭에서 연산을 종료하고자 할 때 사용할 수 있습니다.
# 첫 번째 'cache 0' 스트링 매칭에서 이후 70 라인을 프린트하고 종료
$ cpuid | grep -m1 -A70 'cache 0'
매칭 라인 넘버 프린트
-n, --line-number
매칭 라인을 프린트할 때 해당 라인 넘버도 함께 프린트됩니다.
$ grep -n foo *.c
best.c:40:int foo()
err.c:12:int foo() {
main.c:9:int foo() {
...
출력에 파일명도 포함
-H, --with-filename
-h, --no-filename
grep foo *.c
와 같이 여러 개의 파일을 검색하는 경우는 -H
가 디폴트가 되어
파일명이 함께 표시되고 grep foo hello.c
와 같이 하나의 파일만 검색하는 경우
-h
가 디폴트가 되어 파일명이 표시되지 않습니다.
이것은 find 명령의 -exec 에서 grep 명령을 실행할때 하나의 파일로 인식되어
파일명이 표시되지 않는데 이때 -H
옵션을 사용하면 파일명을 함께 출력할 수 있습니다.
$ find * -name Makefile -exec grep -Hne 'include' {} \;
매칭 라인 카운트
-c, --count
해당 파일에 몇 개의 매칭 되는 라인이 있는지를 알려줍니다.
$ grep -c 'hello' *.html
aaa.html:2
bbb.html:0
ccc.html:1
...
단어 매칭
-w, --word-regexp
스트링의 일부분이 아닌 단어 매칭을 합니다.
파일명만 프린트
-l, --files-with-matches
-L, --files-without-match
이 두 옵션은 매칭 라인을 출력하지 않고 단지 파일명만 출력합니다.-l
옵션은 매칭이 발견되는 파일명을 프린트하고 (첫 번째 매칭에서 스캐닝이 중단됩니다.)-L
옵션은 반대로 매칭이 발견되지 않는 파일명을 프린트합니다.
# ./dir 디렉토리 이하 모든 파일을 검색해서 'hello' 를 포함하고 있을 경우 XXX 를 YYY 로 변경
$ grep -rl 'hello' ./dir | xargs sed -i 's/XXX/YYY/g'
라인 구분자가 NUL 문자일 경우
-z, --null-data
입력되는 데이터가 newline 대신에 NUL 문자를 라인 구분자로 할경우 사용합니다.
프린트 금지
-q, --quiet, --silent
아무 출력도 하지 않고 매칭이 발견되는 즉시 종료 상태 값으로 0 을 리턴하고 exit 합니다.
-s, --no-messages
오류가 발생해도 에러 메시지를 출력하지 않습니다.
이 옵션을 사용하면 명령 실행시 출력이 발생하지 않고 종료 상태 값만 설정됩니다.
따라서 스크립트를 작성할 때 if
문에서 사용하기 좋습니다.
$ AA="foo 12345 bar"
$ if echo "$AA" | grep -Eq '[0-9]+ bar'; then echo 111; else echo 222; fi
111
gzip 파일 grep
zgrep 명령을 이용하면 gzip 파일을 grep 할 수 있습니다.
$ zgrep –i error /var/log/syslog.2.gz
매칭에 escape sequence 를 사용하는 방법
grep 은 regexp 를 작성할 때 기본적으로 [[:blank:]]
같은 character class 만 사용할 수 있습니다.
따라서 매칭에 escape sequence 를 사용하려면 다음과 같은 방법을 이용해야 합니다.
# bash 의 경우 $' ' quotes 을 사용
bash$ echo -e 'hello\tworld' | grep $'\t'
hello world
# sh 의 경우 "$(echo "\t")" 형식 을 사용
sh$ echo 'hello\tworld' | grep "$(echo "\t")"
hello world
# 또는 PRE 를 사용
$ echo -e 'hello\tworld' | grep -P '\t'
hello world
$ sudo ldconfig -v 2>/dev/null | grep -P '^\t'
/usr/lib/x86_64-linux-gnu/libfakeroot:
/lib/i386-linux-gnu:
/usr/lib/i386-linux-gnu:
. . .
. . .
grep 은 binary 파일도 매칭에 사용할 수 있습니다.
grep 이 binary 파일을 다루는 3 가지 방법
1. --binary-files=binary
디폴트로 binary 파일 내에 존재하는 스트링을 검색해서 매칭이 있을 경우 알려줍니다.
텍스트 파일에서처럼 매칭 라인을 출력하지는 않습니다.
$ grep ELF mycomm # mycomm 은 실행 파일
Binary file mycomm matches <--- 매칭이 있다고 알려줌
2. --binary-files=without-match
binary 파일일 경우 매칭을 시도하지 않고 skip 합니다.
이것은 -I
옵션과 같은 것입니다.
3. --binary-files=text
binary 파일도 text 파일처럼 취급하여 매칭하고 출력합니다.
이것은 -a
옵션과 같은 것입니다.
-a, --text
-b, --byte-offset
-b
옵션과 -o
옵션을 이용하면 파일내에 매칭되는 위치의 offset 값을 구할 수 있습니다.
# ':' 왼쪽이 offset 값
$ echo helloworld | grep -bo hel
0:hel
$ echo helloworld | grep -bo ll
2:ll
$ echo helloworld | grep -bo wor
5:wor
$ echo helloworld | grep -bo l
2:l
3:l
8:l
따라서 -abo
옵션을 이용하면 binary 파일내에서 매칭되는 위치의 offset 값을 구할 수 있습니다.
활용 예는 여기 를 참고 하세요
Quiz
다음 명령문은 어디가 잘못되었을까요?
$ cat /etc/mtab | grep cgroup
이것은 grep 명령을 사용하는 사람들 중에서 종종 발견되는 부분인데 명령문이 틀린 것은 아니지만 위 명령은 다음과 같이 할 수 있습니다.
$ grep cgroup /etc/mtab