티스토리 뷰

전체/장난하기

find utility 철학

Coolen 2005. 6. 12. 23:55
find 를 제법 잘 쓰는 사람은 다 아는 얘기일 것 같지만, 흔히들 사용하면서도 그 많은 옵션이 잘 안들어오는 사람들을 위해 정리하고자 글을 써본다.

find의 철학은 그 이름에서 이미 감춰져 있다고 해도 과언이 아니다.
find를 통해서 할 수 있는 것이 고작 원하는 이름의 혹은 원하는 속성을 가진 파일을 찾는 것이라 생각한다면 그것은 정말 누구나 사용할 줄 아는 방법으로서의 find 이다.

find의 철학은 다음과 같다. 어디에 이런 글이 쓰여있는지는 나도 모르겠지만, 경험상 정리하자면,

find 는 true/false에 의한 directory 탐색기이다.

신선한가?



우선 다들 그러하듯이 man find를 한번 수행해보고 true/false라는 말이 얼마나 나오는지 한 번 살펴보시라. 가능하면, 리눅스의 man find와 솔라리스의 man find를 비교하는 것도 좋을 것이다.

살펴보니 리눅스보다 솔라리스에서 더 많은 true/false라는 말을 발견하게 될터인데, 솔라리스 쪽은 의도 중심적인 표현이고 리눅스쪽은 사용자 중심쪽 표현이기 때문일 것이다.

예 1) find /usr/include -name '*.h'

위 문장은 확장자가 .h 인 파일들을 /usr/include에서 찾는 다는 것을 쉽게 알 수 있다. 하지만, man page를 자세히 읽어보면, 이 문장은 다음과 같이 해석될 수 있다.

만약 현재 스캔하고 있는 파일의 이름이 '*.h'에 일치한다면 참이며, 참인 조건으로 더이상의 비교를 하라는 지시가 없으므로 함축된 -print를 수행한다. 즉,
find /usr/include -name '*.h' -print
라고 명시적으로 밝히는 것과 동일하다.

예 2) find /usr/include -name '*.h' -ls

HPUX find에는 -ls 명령이 없지만 솔라리스와 리눅스에는 존재하므로 설명을 드리자면, 위의 문장은 '*.h'에 일치하는 파일에 대하여 ls -l 과 같은 명령을 수행했을 때 처럼 자세한 정보를 출력하게 된다.
-ls를 설명하고자하는 것이 아니라, 이 문장을 true/false 이론(?)에 근거하여 다시 설명하자면,

이름이 '*.h'에 일치하게 되면 ls -l 을 수행한 결과를 화면에 나타내라라는 뜻이다.

즉,

find /usr/include -name '*.h' -a -ls

라는 뜻이다. find 의 여러 연산자 중에 -a, -o 는 각각 AND, OR를 의미한다. man page에는

-and is assumed where the operator is omitted. (linux)

Expression [ -a ] Expression - Concatenation of expressions (the AND operation is implied by the juxtaposition of two primaries or may be explicitly stated as -a) (aix; solaris, hpux도 같다)



라는 것을 알 수 있다. (리눅스에만 존재하는 -and는 -a와 같은 뜻이다.)

즉, 쉽게 사용해 왔던,

find -name '*.h' -exec grep 'size_t' {} \;

이런 류의 것들이 실은

find -name '*.h' -a -exec grep 'size_t' {} \;

와 같이 해석된다는 것이다.


지금까지 눈에 씌여 있던 귀차니즘의 막을 걷는 작업을 하였다.
-print 와 -a 는 실상 find의 철학을 잊고 쉽게 사용할 수 있는 유틸리티로 만드는 일종의 음모였다라고 생각하시라.

자, 다음과 같은 것은 어떻게 표현해야할까? /usr/include 에 수행한다고 가정하고,

  • .h가 들어가지 않은 것들에 대한 리스트
  • grep size_t 로 한 줄도 출력되지 않는 헤더파일들
  • 헤더하나를 a.h 로 복사해 왔는데, 이것과 일치하는 파일

첫번째 것은 두 가지 방법으로 가능하다.
find /usr/include ! -name '*.h'
find /usr/include -name '*.h' -o -print
처음 방법이야 흔히 사용하던 표현식의 반대이므로 바로 이해될 것이고, 두번째는 -o 즉, OR라는 것은 처음것이 거짓인 경우 두번째 것을 해봐야 참값을 알 수 있는 수식으로 C언어나 스크립트 언어들을 익혀 봤다면 흔히 쓰이는 트릭(?)으로 잘 알 수 있을 것이다.

두 번째는 일단, grep 의 exit status가 한 줄이라도 발견하면 정상 값을 돌리고 그렇지 않으면 오류값을 돌린다는 사실을 알아야한다.

$ grep -q size_t /usr/include/stdio.h
$ echo $?

size_t 대신 다른 것으로 확인해보면, 발견되면 정상(0), 없는 것이면 오류(1)를 나타냄을 알 수 있다. solaris의 경우 grep 을 /usr/xpg4/bin/grep 을 사용하도록 PATH를 조절하는 것이 -q를 사용할 수 있다. -q는 quiet의 의미를 가진다. 즉, 우리는 exit status만 관심있다는 의미이다.

그러면 두 번째 질문도 다음과 같이 표현 할 수 있다.

find /usr/include -exec grep -q size_t {} \; -o -print

즉, {}에 치환하여 grep 을 수행해본 결과 참이아니라면, 즉 한 줄도 발견되지 않았다면(-o) 출력하라(-print).

세 번째 질문은 cmp를 사용하면 된다. 여기에도 귀엽게스리 -s 옵션이 있다. (많은 유틸리티든지 이런 용도로 사용되는 옵션이 있다. s는 silent !)

find /usr/include -exec cmp -s a.h {} \; -a -print



이 페이지는 find 옵션들을 설명하려는 것이 아니라 철학을 설명하는 것이므로, 더 가렵더래도 긁어주지 않음을 용서하시라.

find 는 뒤에 붙은 장황한 옵션, 명령들에 대해서 참 거짓을 확실히 정할 때까지 계속 수행하는 것을 염두에 둔다면 -a, -o, 그리고 \(, \)로 사용할 수 있는 여러 조합에 대해 상상의 날개를 펼칠 수 있을 것이다.

다시 말하지만, find 는 true/false를 기반으로하는 디렉토리 탐색기이다.



Tip
많이 쓰일만한 표현으로 CVS와 .deps를 제외하여 search하려면,

find . -name CVS -a -prune -o -name .deps -a -prune -o -print

이렇게 쓸 텐데,

이 의미는, CVS 를 만나면 -prune 을 수행해봐야 알 수 있고 (-a 이므로),
-prune 은 man page에서 true를 되돌린다고 되어 있으므로,
여기까지의 계산식은 참이되며, 다음이 -o 이므로 더이상 계산할 필요가 없이 끝내게 된다.

-a 는 언제든지 생략가능하므로,

find . -name CVS -prune -o -name .deps -prune -o -print
find . \( -name CVS -o -name .deps \) -prune -o -print

로 사용가능하다.



반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함