find

find

GNU find는 디렉토리 트리를 따라 각각의 파일 이름을 주어진 표현식들에 적용하여 매칭되는 결과를 프린트하거나 이를 이용해 별도의 명령을 실행할 수 있다.

사용법

find [-H] [-L] [-P] [-D debugopts] [-Olevel] [path…] [expression]

옵션

-H, -L, -P는 심볼릭 링크를 어떻게 다룰 것인지를 결정한다. 이후에 오는 인자들은 시험할 파일이나 디렉토리의 이름들이다. 이후에 -, (, !로 시작하는 인자를 만나면 여기서부터는 조건식으로 평가되며 이들은 찾고자 하는 조건이 된다. 만약 경로가 주어지지 않으면 현재 경로를 기준으로 찾는다. 또한 표현식이 주어지지 않으면 기본적으로 -print가 지정된다.

심볼릭링크

하나 이상의 옵션이 붙으면 맨 뒤의 옵션이 적용된다.

-P: 심볼릭 링크 무시

-L: 심볼릭 링크를 따라간다.

-H: 명령줄 인자를 제외한 심볼릭 링크를 따르지 않는다.

디버그 옵션

-D debugopts:

  • help: 디버그 옵션을 보여준다.
  • tree: 표현식 트리를 보여준다.
  • stat: 파일들에 대한 stat 시스템 콜을 보여준다.
  • opt: 표현식 트리에 대한 분석 정보를 표시한다.
  • rates: 예측치에 대한 평가를 표시한다.

-Olevel: 최적화 정도를 지정할 수 있다.

표현식

표현식은 옵션들과 테스트 그리고 액션으로 구성된다. -and는 생략된 연산자로 취급한다.

옵션

-d: -depth의 약어. 해당 디렉토리를 처리하기 이전에 하위 디렉토리를 먼저 처리하도록 한다. -delete 액션은 이 옵션을 기본적으로 갖는다.

-daystart: 시간 관련 테스트(-amin, -atime, -cmin, -ctime, -mmin, -mtime)의 기준 시간을 24시간 이전이 아닌 오늘 자정으로 쓴다.

-maxdepth levels: 하위 디렉토리 처리를 제한한다.

-mindepth levels: 주어진 디렉토리만큼 내려간 디렉토리만 처리한다.

-noleaf: UNIX 파일 시스템이 아닐 때. 이는 디렉토리가 기본적으로 이름과 “.” 이라는 두 개의 노드를 가지고 있다고 가정하고 최적화하는데, 이 옵션은 이 최적화를 쓰지 않는다. MS-DOS 등에서 성능이 좋아진다.

-regextype type: 정규식 사용시의 정규식의 타입. emacs가 기본이며, posix-awk, posix-basic, posix-egrep, posix-extended 등에서 고르면 된다.

TEST

-newerXY-samefile과 같은 일부 테스트들은 현재 파일과 주어진 파일을 비교한다. 테스트는 반복되겠지만, 참고 파일은 첫 명령 입력시에 파싱되어 한번만 쓰인다.

아래에 n으로 쓰는 것은 숫자값인데 앞에 +/- 붙어서 보다 크다, 보다 작다를 의미할 수 있다.

-amin n : n 분 전에 최종 액세스한 파일

-anewer file: 주어진 파일이 수정된 시각보다 이후에 액세스된 파일.

-atime n: n * 24시간 이전에 최종 액세스된 파일. 따라서 atime +1 하면 이틀 전에 액세스한 파일이 된다.

-cmin n: n 분 전에 변경된 파일

-cnewer file: 주어진 파일의 수정 시간 이후에 수정되었는지

ctime n: n * 24 시간 이전에 변경된 파일.

-empty: 빈 파일 혹은 디렉토리

-executable: 실행가능한지

-false : 무조건 실패

fstype type: 파일 시스템 타입

-ipath pattern: 쓰이지 않으며 -iwholename과 동일하다.

-iregex pattern: 대소문자 구분없는 정규식 매칭

-iwholename pattern: -wholename과 같으나 대소문자 무시

-mmin n: 파일이 n 분 전에 수정됨

-mtime n: 파일이 어제기준 24 * n 시간 전에 수정됨.

-name pattern: 파일의 베이스 이름(디렉토리 이름 뗀)이 주어진 패턴과 매치하는지 본다.

-newer file: 주어진 파일보다 최근에 수정되었는지

-newerXY reference: 주어진 레퍼런스보다 최근인지

-path pattern: 전체 경로가 주어진 패턴인지 본다. 이 때, ‘/’, ‘.’ 는 특별한 의미를 가지지 않는다.

-regex pattern: 파일의 전체 경로를 주어진 패턴과 매치되는지 검사한다.

-samefile name: 주어진 이름의 파일과 같은 파일인지 검사한다.

-size n[cwbkMG]: 특정 크기인지 본다. 보다 크기거나 작거나를 정할 수 있다.

-true: 항상 참

-type c: 타입을 검사한다.

-used n : 상태가 변경된지 n 일 이후에 사용되었는지를 확인한다.

-wholename pattern: 전체 경로를 비교한다. -path와 같은 옵션이다.

액션

기본적으로 액션은 파일 명들을 출력한다.

-print : 기본 액션. 테스트를 통과한 파일명들을 출력한다.

-fprint FILE: 파일에 결과를 쓴다.

-ls: ls -dils 포맷을 출력해준다.

-fls FILE: 파일에 -ls 옵션의 결과를 쓴다.

-print0: 개행이 아닌 널 문자로 구분하여 출력한다. 이는 xargs로 결과를 넘겨서 쓸 때 유용하다.

-printf, -fprintf FORMAT: 주어진 포맷에 맞추어 출력한다.

-prune : 만약 발견한 파일이 디렉토리라면, 그 아래로 내려가지 않는다. 만약 -depth가 있다면 취소된다.

-delete: 발견된 모든 결과를 제거한다. 사용시 매우 주의가 필요하며 (까딱 하위 디렉토리 내 파일까지 다 날려버린다든지…) 꼭 출력해 본 다음에 실행할 것

-exec COMMAND ; : 주어진 명령을 실행한다. ;으로 꼭 끝나야 하는데 이는 이스케이프될 필요가 있다. (MS-DOS에서는 안해도 되더라) 파일의 이름이 들어갈 자리는 {} 으로 지정해주면 된다.

-exec COMMAND {} + : 이는 각각의 파일 이름을 뒤에다 붙이는 식으로 파일 개수보다 생성되는 명령의 수가 적다.

-execdir COMMAND ; : -exec는 시작 디렉토리 기준으로 명령을 실행하는데 -execdir은 매 서브 디렉토리에서 명령을 수행한다.

연산자

우선순위가 높은 것부터 쓴다. 위의 표현식들은 EXPR로 표기한다.

( EXPR ): 괄호로 둘러싼 영역을 우선 평가한다.

! EXPR, -not EXPR : 결과를 반대로

EXPR1 EXPR2: 두 표현식이 나란히 써지면 AND로 연결한다.

EXPR1 -a EXPR2 : AND로 연결

EXPR1 -o EXPR2 : OR로 연결한다. 만약 앞쪽의 내용이 참이면 쪽은 평가되지 않는다.

EXPR1 , EXPR2 : 두 표현식을 리스트로 만든다. 앞 표현식의 값과 상관없이 두 개의 표현식은 항상 평가되며, 앞의 결과는 그냥 무시된다.

예제

find /tmp -name core -type f -print | xargs /bin/rm -f

/tmp 하위의 core라는 이름의 파일을 모두 찾은 다음 이들을 몽땅 지운다. 단, 이 명령은 파일 이름에 공백이 들어있거나 하면 xargs에서 파싱을 잘못하므로 실패할 가능성이 있다.

find /tmp -name core -type f -print 0 | xargs -0 /bin/rm -f

위에서 생기는 문제를 해결할 수 있다.

find . -type f -exec file {} \;

모든 파일을 찾아서 file 명령을 적용한다.

find / \
\( -perm -4000 -fprintf /root/suid.txt %#m %u %p\n \) , \
\( -size +100M -frpintf /root/big.txt %-10s %p\n \)

모든 파일을 찾아서

  1. 권한값이 4000 미만이면 /root/suid.txt 파일에 기록하고
  2. 크기가 100MB 이상이면 /root/big.txt에 기록한다.

액션 역시 표현식이므로 이런식으로 두 분류를 지정할 수 있다. 좀 더 쉽게 만들어보면

find /d/pics \( -type f -name *.jpg -fprint jpg.txt \) , \
\( -type f -name *.png -fprint png.txt \)

이런식으로 써서 한 번의 탐색으로 JPG 파일과 PNG 파일을 분류해서 각각의 결과를 개별 파일에 저장하는 것이 가능하다.

find $HOME -mtime 0 으로 홈 디렉토리 내에서 24시간 이내에 수정된 파일을 찾을 수 있다.

find /sbin /usr/sbin -executable \! -readable -print -> /sbin, /usr/sbin 에서 실행 가능하지만, 읽을 수 없는 파일을 찾아서 출력

find . -perm 664 : 664 권한인 파일 찾기

cd /source-dir
find . -name .snapshot -prune -o \( \! -name *~ -print0 \) | cpio -pmd0 /dest-dir

제법 복잡해 보이는데, 이 명령은 /source-dir의 파일들을 /dest-dir로 복사한다. 하지만 이름이 .snapshot인 파일이나 디렉토리(그리고 그 안의 내용)은 생략한다. 표현식 부분만 평가해보자.

-name .snapshot  # 이름이 .snapshot 인가?
   -prune        # 디렉토리라면 더 이상 들어가지 마라. 그리고 true이다.
     -o            # 앞이 true 니까 뒤도 평가한다. 
       \( \! -name *~ # 백업파일이 아니면
                 -print0 # 널문자로 구분하여 출력한다. 
       \)

-o의 앞쪽의 표현식은 -prune 으로 끝났으므로 일단 print 되지 않는다. 그리고 -prune은 일단 true값이므로 뒤로 이어져서 백업이 아닌 파일들이 프린트되는 셈이다.

만약 .vim 디렉토리에서 번들의 .git 을 제외하고 탐색하는 방법을 이를 응용해보면

find ~/.vim -name .git -prune -o -type f -name *.vim -print

보다 이해가 쉽게 추가하면

find ~/.vim -name .git -prune -fprint skipped.txt -o -type f -name *.vim -print

이런식으로 쓸 수 있다.

실제로 앞쪽에 .git을 들어가지 않도록 하는 가드 절이 없으면 번들 디렉토리의 탐색은 10초 가량이, 가드 절이 있는 경우에는 1초 가량이 걸렸다.

find의 일부 예제를 보고 “위치 > 조건매칭 > 결과에 대한 조작”으로 생각하기 쉬운데 여러 액션들 역시 “평가식”이다. 따라서 다음과 같은 조작도 가능한데,

find repo/ \
-exec \test -d {}/.svn -o -d {}/.git -o -d {}/CVS ; \
-print -prune

이 명령을 보자. 평가 자체를 -exec로 하고 있다. 여기서 쓰인 -otest 명령의 옵션이다. (역시 의미는 OR)

따라서 주어진 이름 뒤에 CVS, .svn, .git 이 붙어 있는 디렉토리가 있으면 이를 출력하고 해당 디렉토리로는 들어가지 않는다는 뜻이다.

repo/project1/CVS
repo/gnu/project2/.svn
repo/gnu/project3/.svn
repo/gnu/project3/src/.svn
repo/project4/.git