메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

한빛랩스 - 지식에 가능성을 머지하다 / 강의 콘텐츠 무료로 수강하시고 피드백을 남겨주세요. ▶︎

IT/모바일

유닉스에서 find 기능 이용하기 II

한빛미디어

|

2002-05-17

|

by HANBIT

15,184

저자: 드루 라빈(Dru Lavigne), 역 서성용

지난 기사에서 필자는 유닉스에서 find 명령을 사용하는 것에 대해 소개했습니다. 이번 시간에는 이 편리한 명령으로 사용할 수 있는 몇 가지 스위치들을 설명해 보겠습니다.

지난 기사에서 마지막으로 살펴보았던 예제로 시작하겠습니다.
find . -atime +7 -o -size +`expr 10 \* 1024 \* 2` -print
다시 요점만 말하면 이 명령은 현재 디렉토리와 그 서브디렉토리에서 .으로 표현되는 7일 이상 접근되지 않은 (-atime +7)이나 (-o) 파일을 찾는 것으로 이들은 특정 크기보다 (-size +) 더 큽니다. 필자는 expr 명령을 사용해서 필요한 크기를 계산했습니다. 10MB를 목표로 두고 있었고 find는 512 바이트 단위로 생각하므로 10ⅹ1024ⅹ2를 계산해야 했습니다(2ⅹ512 는 1024 이므로).

여기서 필자가 ` 기호 혹은 "backquote(역 인용부호)"를 사용했다는 것에 주목합시다(PC 키보드에서 가장 왼쪽에 있는 키). 유닉스에서 한 명령의 출력을 다른 명령으로 전달하려면 출력 보내고 싶은 명령을 backquotes 사이에 써주면 됩니다(이것은 명령 치환으로 알려져 있음). 내가 계산하고 싶었던 수식을 backquotes 사이에 넣어주기 때문에 계산 결과는 -size 스위치에 전달되며 find 명령에 의해 사용됩니다.

그리고 주목해주기 바라는 마지막 사항은 명령에서 \ 문자를 사용해 두 개의 *를 사용했다는 것입니다. 수식 계산에서 *는 곱하기를 나타내지만 셸에서는 와일드카드를 나타냅니다. * 앞에 \를 놓았기 때문에 셸은 \을 와일드카드로 해석해지 않을 것이며, expr*를 받을 것이고 곱하기를 계산해 줄 것을 알게 될 것입니다.

다른 예제를 실행해봅시다. 거대한 디렉토리 구조를 가지고 있고 특정 패턴에 대해 검색을 하여 이 패턴을 만족시키는 모든 파일들을 지우기를 원한다고 합시다. find 명령으로 이를 수행하는 여러 가지 방법이 있으므로 이러한 방법을 비교해 보도록 합시다.

홈디렉토리에 tmp라는 디렉토리가 있는데 이 안에는 tst라는 서브디렉토리가 있습니다. 이 tst 디렉토리에는 아주 많은 파일과 서브디렉토리가 있으며 이러한 파일 중 일부는 .old 확장자로 끝납니다. tst 디렉토리 내에 몇 개의 파일이 있는지 알아보는 것으로 시작합시다.
cd ~/tmp/tst
find . -print | wc -l
   269
find 명령이 실행될 때 발견된 각각의 파일을 서로 다른 라인에 print했다는 것에 주목합시다. 그리고 난 후 그 결과를 파이프해서 행의 수를 세는 (-l) 스위치를 이용해 word count(wc) 명령으로 전달했습니다. 이것은 tst 디렉토리에 269 개의 파일(디렉토리 포함, 유닉스에서 디렉토리는 실제로 파일이기 때문)이 있음을 알려줍니다.

이 중 몇 개의 파일이 .old 확장자를 갖는지 알아봅시다.
find . -name "*.old" -print | wc -l
   67
이제 이 *.old 파일들을 삭제하기 위해 무엇을 해야 할까요? 한가지 방법은 -exec 스위치를 이용해서 그것이 rm 명령을 다음과 같이 호출하도록 하는 것입니다.
find . -name "*.old" -exec rm {} \;
이 작업이 끝나면 이 명령을 반복해서 *.old 파일들이 남아있는지 확인할 수 있습니다.
find . -name "*.old" -print | wc -l
   0
이 명령이 잘 동작하기는 하지만 여러 개의 파일들을 지우는 최선의 방법은 아닙니다. 여러분이 -exec 스위치를 사용할 때 마다 find가 발견하는 모든 파일에 대해 개별적인 프로세스가 생성됩니다. 만약 가정용 컴퓨터에서 소량의 파일에 대해 find를 할 때는 문제가 되지 않겠지만 제작 시스템에서 수백 개나 수천 개의 파일에 대해 find를 한다면 문제가 될 것입니다. 어쨌든 이 방법은 다른 방법보다 보다 많은 자원을 소모하기 때문에 느립니다.

이러한 파일을 지우는 두 번째 방법을 살펴봅시다. 이번에는 xargs를 사용해 보았습니다.
find . -name "*.old" -print | xargs rm
여기서 필자가 find 명령 끝에 \;를 포함하지 않아도 되었다는 것을 발견할 것입니다. 참고로 그 문자열은 exec에 넘겨지는 명령을 종료하기 위해 사용됩니다. 이 명령에서 xargs를 사용하여 필자는 여전히 확장자가 .old로 끝나는 모든 파일을 지울 것입니다. 발견되는 각각의 파일을 생성하는 것이 아니라 xargs를 통해 오직 한 개의 프로세스만 시작될 것입니다. find가 각각의 파일을 발견함에 따라, 각각의 행에 각 파일의 이름이 오는 목록을 생성할 것입니다. 이 목록은 xargs로 넘겨져서 파일 목록의 모든 행을 받아들여 각 파일을 스페이스로 구분하면서 한 줄로 만들 것입니다. 그리고 나서 xargs는 이 파일 목록 인자를 rm 명령에 넘겨줍니다.

사실 이러한 파일을 삭제하는 세 번째 방법도 있는데, find에 -delete 스위치를 사용하는 것입니다.
find . -name "*.old" -delete
이 명령은 가장 사용하기 쉬운 문법이며 실제로도 파일을 지우는데 가장 효율적인 방법입니다. -delete 스위치는 개별적인 프로세스를 생성할 필요도 없습니다. 모든 파일들은 find 프로세스에 의해 삭제되기 때문입니다. 게다가 이 명령은 언제나 잘 작동할 것입니다. 반면에 xargs 명령은 find가 인자 목록(argument list)으로 명령에 전달될 수 있는 것보다 많은 파일을 발견할 경우 실패할 것입니다. 만약 여러분이 복잡한 디렉토리 구조를 검색하고 있거나 파일명이 아주 길다면 이 제한에 도달할지도 모릅니다. 실제 제한 값이 궁금하다면 설정된 sysctl 값을 확인하면 됩니다.
sysctl -a | grep kern.argmax
kern.argmax: 65536
65536은 인자 목록에서 최대 바이트(글자) 수를 나타냅니다.

다른 스위치들을 알아보기 전에 find가 파일을 지우기 전에 어떤 파일들이 지워지는지 확인하고 싶을지도 모른다는 것을 언급해야겠군요. 필자가 든 예제에서는 시험용 디렉토리 중의 하나에 있는 오래된 파일을 지우려 했습니다. 만약 find가 원하지 않은 파일들을 지우지 않을까 걱정된다면 다음의 명령을 먼저 실행해 보시기 바랍니다.
find . -name "*.old" -print
이것은 모든 해당 파일의 목록을 보여줄 것입니다. 목록이 적당하다면 -delete 스위치를 사용해 위에서 언급한 예제에서처럼 파일들을 지우면 됩니다.

그렇지 않으면 다음과 같이 -ok를 사용해 위와 똑같은 작업을 하나의 find 명령으로 끝낼 수 있습니다.
find . -name "*.old" -ok rm {} \;
-ok는 뒤에 나오는 명령을 실행하기 전에 확인을 위해 물어볼 것입니다. rm 명령을 사용해야 한다는 점에 주목하세요. -delete 스위치는 사용할 수 없기 때문입니다. 그리고 -exec를 사용하는 것처럼 {} \; 문법을 사용해야만 -ok가 제대로 동작할 것입니다.

다른 스위치를 살펴봅시다. -ls 스위치는 발견되는 각각의 파일에 대해 inode 번호, 블록 개수, 퍼미션, 하드링크 개수, 소유자, 그룹, 바이트 단위 크기, 최근 수정시간, 이름을 알려줄 것입니다. 예를 들어 다음의 명령은 홈 디렉토리에 있는 첫 10개의 디렉토리를 보여줄 것입니다. -type d 스위치를 사용하여 오직 디렉토리만을 보고자 했다는 것을 명시했음에 주목하세요.
cd
find . -type d -ls | head
976142  8 drwxr-xr-x  39 genisis  wheel    4096 Mar  3 17:52 .
1413099  2 drwxr-xr-x  2 genisis  wheel   512 Mar  3 13:38 ./pdfs
373539  2 drwxr-xr-x  2 genisis  wheel    512 Feb  6 12:38 ./tst
1087249  2 drwxr-xr-x  2 genisis  wheel   512 Oct  4 07:29 ./perlscripts
650764  2 drwx------  2 genisis  wheel  512 Mar  3 17:52 ./mail
706616  2 drwx------  4 genisis  wheel  512 Sep 22 14:29 ./.kde
706635  2 drwx------ 11 genisis  wheel  512 Nov  7 12:36 ./.kde/share
706636  4 drwx------  3 genisis  wheel  1536 Mar  2 18:38 ./.kde/share/config
785986  2 drwx------  2 genisis  wheel  512 Sep 22 14:36 ./.kde/share/config/colors
706682  2 drwx------  3 genisis  wheel  512 Mar  2 18:36 ./.kde/share/fonts
-ls 스위치를 이용해 조금 더 보기좋게 꾸며봅시다. 기사 앞부분에서 우리는 출력을 wc -l로 파이프(pipe)하여 얼마나 많은 파일들이 특정 표현을 포함하는지 알아보았습니다.
find . -type d -print | wc -l
   256
실제로 255 개의 서브디렉토리가 있는 것으로 출력되는데 256중에 한 개는 현재 디렉토리이기 때문입니다. 이제 이 명령을 이용해서 이 디렉토리 스트럭처가 펼쳐져 있는지 잘 알아봅시다.
find . -type d -ls | awk "{print $4 - 2, $NF}" | sort -rn | head
37 .
26 ./.kde/share/apps/kio_http/cache
18 ./.kde/share/apps
15 ./.gimp-1.2
9 ./tmp/tst
9 ./.kde/share
8 ./tmp/tst/h
8 ./tmp/tst/g
8 ./tmp/tst/f
8 ./tmp/tst/e
정말 훌륭하지 않습니까? 홈디렉토리(.)에는 37개의 서브 디렉토리가, .kde/share/apps/kio_http/cache 서브디렉토리에는 26 개의 서브디렉토리와 그 외에 다른 서브디렉토리들이 있는 것으로 나타났습니다. 이제 이 find 명령이 어떻게 동작했는지를 알아봅시다. 필자는 -ls 스위치를 이용하여 시작했는데, 이 스위치는 발견되는 각각의 디렉토리에 대해 적절한 정보를 줍니다. 이 정보는 awk 유틸리티로 파이프 되었으며 이는 특정 필드에서 자료를 추출해내기 위해 사용되는 유틸리티입니다. 원래의 -ls 출력에서 결과들이 inode 번호, 블록의 개수, 퍼미션, 링크의 수 등등의 특정한 필드로 출력되었던 것을 기억할 것입니다. awk에게 4번째 컬럼(링크의 개수를 포함하고 있으며 awk에는 $4)에서 정보를 가져다가 그 값에서 2를 빼도록 했습니다(디렉토리 . 이나 .. 에는 관심이 없기 때문에). 게다가 각 디렉토리의 이름도 알고 싶었습니다. 이것이 맨 마지막 칼럼이기 때문에 그 필드를 나타내기 위해 $NF를 사용했습니다. 이러한 명령들을 괄호 {} 안에 나타냄으로써 awkfind 명령으로부터 받는 모든 파일에 대해 이 작업을 하도록 했습니다. 그리고 난 후 awk에서의 결과를 -rn을 사용하여 sort 명령으로 파이프 했습니다. sort가 수의 출력을 가장 큰 것에서부터 작은 것으로 정렬하여 나는 어떤 디렉토리가 가장 많은 서브디렉토리를 갖고 있는지 볼 수 있었습니다. 나는 이 결과를 모두 출력해서 여러분을 지루하게 하고 싶지 않았기 때문에 최종 결과를 head 명령으로 파이프하여 첫 번째 10가지만을 출력했습니다.

다른 find 스위치로는 -perm 이 있습니다. 아래의 예제는 퍼미션이 777인, 즉, 모두에게 읽기, 쓰기, 실행이 허가된 모든 파일을 찾는 것입니다. 이것은 아래의 명령으로 쉽게 할 수 있습니다.
find . -perm 777 -print
위의 명령은 정확하게 777 퍼미션을 가진 파일만을 검색합니다. 만약 모든 퍼미션 비트가 아니라 특정 비트에만 관심이 있다면, 다음과 같이 할 수 있습니다
find . -perm -4000 -print
이 예제는 SUID 비트가 설정된 파일들만을 출력할 것입니다. (이전 기사에서 퍼미션에 대해 더 많이 읽어보세요.) 또다른 편리한 find 명령은 이것입니다.
find . -perm -0002 -print
이것은 다른 사람들이 쓸 수(writable) 있는 모든 파일을 찾을 것입니다. -0002, -002, -02, -2를 사용해도 앞에 있는 0이 있는 것으로 가정되어 같은 결과를 얻는다는 것에 주목하세요.

오늘의 기사에서 다루려고 하는 마지막 두 개의 스위치는 백업하거나 디렉토리 구조를 복제할 때 유용합니다. -depth부터 시작해 봅시다. 홈 디렉토리 전체를 /backup으로 마운트된 디렉토리에 백업하고 싶다고 합시다. 다음과 같이 할 수 있습니다.
find . -depth -print | cpio -dump /backup
이 명령은 -depth 스위치 없이도 잘 동작하겠지만 항상 그런 것은 아닙니다. 기본값으로 findfind 명령에서 언급된 지점(내 경우엔 . 이나 나의 홈 디렉토리)에서부터 시작해서 발견된 파일들의 목록을 출력합니다. 즉, 그것이 첫 번째 디렉토리를 출력하고 난 후에야 그 디렉토리의 내용을 출력한다는 것입니다. 만약 find가 읽기 전용의 퍼미션을 가진 디렉토리와 마주친다면 find는 여전히 그 디렉토리의 내용의 목록을 cpio 명령으로 전달하겠지만 cpio 명령은 그 서브디렉토리에 있는 파일을 복제할 권한이 없기 때문이다. cpio가 여전히 디렉토리를 생성할 수 있겠지만 디렉토리를 생성함에따라 cpio는 퍼미션을 읽기 전용으로 설정하고, 그 디렉토리 아래에는 어떠한 파일도 생성할 수 없을 것입니다.

그러나 -depth를 기억해 낸다면 find는 가장 낮은 레벨에서 검색을 시작할 것입니다. 이는 디렉토리 자체 목록을 출력하기 전에 디렉토리의 내용을 출력할 것을 의미합니다. 이것은 cpio가 부모 디렉토리에 읽기 전용 퍼미션을 설정하기 전에 이미 파일들이 복제됨을 의미합니다.

만약 전체 홈 디렉토리가 아닌 일부 디렉토리만 복제하고 싶다면 어떻게 해야 할까요? 이것은 -prune 스위치를 사용하면 됩니다. tmp 디렉토리를 제외한 홈 디렉토리 전체를 백업하고 싶다고 합시다. 다음과 같이 할 수 있습니다.
find . -type d -name tmp -prune -o -print | cpio -dump /backup
문법이 다소 어색하게 보일 겁니다. -name 스위치를 사용해서 tmp라는 이름의 모든 디렉토리를(-type d)를 찾아서 그 목록을 -prune에 넘겨주었습니다. 그리고 난 후 논리적 or-o를 사용해서 그 외의 모든 것이 프린트되게 하고 cpio로 파이프 되었습니다.

필자는 지난 두 기사에서 언급한 예제로 여러분들이 find 명령과 문법에 대해 보다 친숙해지는데 도움이 되었기를 바랍니다. 다음 기사에서는 cpio 명령으로 시작해서 tar 명령에 대한 사용법을 비교해보도록 하겠습니다.
드루 라빈(Dru Lavigne)은 캐나나 온타리오주 킹스턴에 있는 공과 전문대학교에서 네트워크를 가르치고 있다. 하나의 테스트 머신에서 얼마나 많은 운영체제를 멀티 부팅할 수 있는지 알아보는 것으로 유명하다.
TAG :
댓글 입력
자료실

최근 본 상품0