Wireframe

GNU find 명령 사용법

GNU find는 디렉토리 트리를 따라 각각의 파일 이름을 주어진 표현식들에 적용하여 매칭되는 결과를 프린트하거나 이를 이용해 별도의 명령을 실행할 수 있다. 간단한 파일 찾기 명령이라고 생각할 수 있지만, find는 실제로 엄청 다양한 일을 할 수 있으며 쉘을 통한 시스템 관리에 있어서 가장 유용하고 필수적인 도구라 할 수 있다. 실제로 grep의 경우 이를 drop in으로 대체하려는 프로젝트들도 많이 있지만, GNU find의 경우에는 이를 100% 커버할 수 있는 대체품이 아직 나오지 않은 상황이다. fd라는 툴이 빠른 속도와 편의성을 개선하여 인기를 얻고 있지만 모든 기능을 대체할 수는 없다.

참고로, GNU find는 그 이름에 있어서 너무 일반적인 동사를 사용하고 있어서 관련 자료를 찾으려 할 때 상관없는 문서가 너무 많이 검색된다. 이 때에는 “GNU find”라고 검색하는 것이 도움이 될 것이다. 이 글의 이후에서는 find를 쓰도록 할 것이다. 또한 윈도에서도 find.exe 라는 도구가 포함되어 있는데, 이것은 grep 처럼 파일에서 텍스트를 검색하는 도구이다. msys를 설치하면 윈도 명령 프롬프트에서도 GNU find를 사용할 수 있지만, 둘의 이름이 갖기 때문에 한쪽의 이름을 바꿔둬야 한다.

사용법

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

find는 단순히 파일을 찾는 것 뿐만 아니라, ‘어떻게 처리할 것인가’에 대한 표현식을 처리할 수 있는 작은 스크립트 언어처리기처럼 작동할 수 있다. 명령의 구조는 링크를 다룰 옵션과 디버그 옵션, 최적화수준 정도의 간단한 옵션이 있으며, 시작 위치를 기준으로 여러 표현식으로 필터링과 처리 방법을 지정하게 된다.

옵션

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

심볼릭링크

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

-P: 심볼릭 링크 무시

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

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

디버그 옵션 및 최적화 수준

-D debugopts:

-Olevel: 최적화 정도를 지정할 수 있다. (사실 정확히 무슨 의미인지는 모르겠당…)

표현식

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

옵션

TEST

액션

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

연산자

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


예제

몇 가지 실행 예제를 소개하겠다. 다음은 가장 간단한 형태로, 특정 경로 아래에 있는 파일들을 모두 삭제하는 명령이다.

특정 파일들을 한꺼번에 삭제하기 (배치실행 방식과 반복실행 방식)

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

/tmp 하위의 core라는 이름의 파일을 모두 찾은 다음 이들을 몽땅 지운다. 단, 이 명령은 파일 이름에 공백이 들어있거나 하면 xargs에서 파싱을 잘못하므로 실패할 가능성이 있다. 이 문제를 방지하려면, 각각의 결과 이름을 공백으로 분리하지 않고 NULL문자로 대체하여 다음과 같이 실행한다.

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

파이프와 xargs를 이용하면, 모든 결과를 취합하여 이를 하나의 명령으로 처리하게 되는데, 이 방식 외에도 find는 -exec 액션을 사용하여, 매 결과에 대해서 각각 명령을 처리할 수 있다.

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

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

복합 명령 적용하기

다음의 예제는 일종의 분기를 구현한 것이다. 콤마로 연결한 두 표현식(액션)은 앞 표현식의 결과에 상관없이 모두 평가되며 최종적으로 뒤의 것으로 평가된다. 이를 통해 한 번의 find 명령으로 여러 가지 액션을 선택적으로 취하게 할 수 있다.

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

위 명령은 ( ) 를 사용해서 두 개의 액션을 순차적으로 실행해서, 한 번의 명령으로 두 번의 find를 실행한 것과 동일한 효과를 낸다. 대신 전체 탐색은 한 번만 수행하므로 두 번 실행하는 것보다는 수행 시간이 훨씬 짧다.

  1. 첫 번째 그룹에서는 권한값이 4000 미만인지를 기준으로 필터링하고, 그 결과를 /root/suid.txt 파일에 기록하며,
  2. 두 번째 그룹에서는 크기가 100MB 이상이면 /root/big.txt에 기록한다.

find에서 모든 액션은 표현식이므로 이런식으로 조합하여 다양한 효과를 낼 수 있다. 다음 명령의 경우에는 JPG파일과 PNG 파일을 찾아서 각각 다른 파일에 그 이름을 기록해준다.

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

시간과 관련된 필터링

-*time, -*min 으로 최근에 수정/액세스한 파일을 필터링할 수 있으며, -anewer, -mnewer 등으로 특정 파일보다 이후에 액세스되었거나 수정된 파일을 찾을 수 있다.

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

권한과 관련된 필터링

find /sbin /usr/sbin -executable \! -readable -print 

/sbin, /usr/sbin 에서 실행 가능하지만, 읽을 수 없는 파일을 찾아서 출력. \!-not과 같은 연산으로 -not -readable로 해석할 수 있다. 다음과 같이 -perm을 사용하여 특정 권한의 파일만 찾을 수도 있다.

find . -perm 664 

다음의 예제는 조금 복잡해 보일 수 있는데…. 특정 조건에 맞는 파일을 다른 위치로 백업하는 명령이다.

cd /source-dir
find . -name .snapshot -prune -print0 -o \( \! -name *~ -print0 \) | cpio -pmd0 /dest-dir
  1. -name .snapshot : 이름이 .snapshot 인지 체크한다.
  2. -prune : 디렉토리라면 그 아래로 내려가지 않는다. (해당 디렉토리만 선택된다)
  3. -print0 : 그리고 해당 디렉토리를 출력한다.
  4. -o : OR
  5. \( -not -name "*~" -print0 \) : .snapshot 이 아니라면 이름이 ~으로 끝나지 않는 모든 파일을 NULL문자종결로 출력한다.
-name .snapshot  # 이름이 .snapshot 인가?
      -prune     # .snapshot 이 디렉토리라면 더 이상 하위 레벨로 들어가지 마라.
      -o         # 이름이 .snapshot이 아닐 때만 true 이다.
       \( \! -name *~  # 백업파일이 아니면 (이름이 ~로 끝나지 않는다면)
             -print0   # 널문자로 구분하여 출력한다.
       \)

즉 이 명령은 현재 위치에서 하위 경로의 .snapshot 디렉토리와, 그 외의 파일 중에서는 백업 파일이 아닌 모든 파일을 다른 곳으로 복사하여 백업하는 명령이다. 전체적으로 상당히 복잡한데, 조금 더 간단한 예를 들어보자.

만약 .vim 디렉토리에서 번들의 .git 을 제외하고 탐색하는 방법을 이를 응용해보면 다음과 같이 쓸 수 있다. (다만, 각각의 .git 은 출력된다.)

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

.git 디렉토리들을 화면에 출력하지 않으려면 -fprint로 임의의 파일로 보내어 화면에는 .vim 파일들만 보이게 할 수 있다.

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

물론, .git 내부에는 .vim 파일이 들어있지는 않겠지만, -prune을 사용하여 가드하면 해당 디렉토리 아래를 훑어보는 과정을 생략하기 때문에 전체 수행 시간을 매우 단축할 수 있다.

복해서 말하지만, 각각의 액션들은 모두 평가식이며 -a , -o 및 , 연산자를 통해서 조합할 수 있다. 따라서 다음과 같은 식으로도 명령을 구성할 수 있다.

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

두 번째 라인 전체가 -exec 평가식이며, 여기서 쓰인 -otest 명령의 옵션이다. (역시 의미는 OR) 액션은 명령이 에러 없이 끝나면 항상 참으로 평가되므로,  -exec 절에 의해서 test 명령이 true (0)를 리턴했다면, 이 절은 참으로 평가된다. 이어지는 -print -prune은 암묵적으로 and로 연결된다.

따라서 test 명령으로 조건을 체크하고 참인 경우에만 뒤의 절들이 실행되며, 이들 디렉토리는 이름만 출력하고 내부로 내려가지는 않게 될 것이다.


Exit mobile version