2012년 5월 17일 목요일

GNU find

출처 : http://beyondtop.egloos.com/10601955


Linux의 find 명령은 Linux 명령어 가운데 가장 유용하면서도 혼란스러운 명령어 가운데 하나로, 다른 명령어의 표준적인 구문과 다른 구문을 가지고 있다는 점에서 어렵다. 하지만, 파일명, 파일 유형, 사용자, 더 나아가 타임 스탬프 별로 파일을 찾을 수 있다는 점에서 powerful command다.
find 명령을 사용하면 이러한 속성을 자유롭게 조합해 파일의 위치를 찾을 수 있을 뿐만 아니라, 찾은 파일에 대해 또 다른 연산(명령어의 적용)을 수행할 수 있다.

기본 사용법

먼저, find 명령의 기본 구조는 다음과 같다.
find   start_directory  test  options   criteria_to_match action_to_perform_on_results
                          
다음 명령은 “java”라는 확장자를 가지고 있는 모든 파일을 PWD 내에서(하위 디렉토리 포함) 찿는다.
find . -name  "*.java"   
아래 명령어 역시 동일한 결과를 나타내게 되는데, 어떤한 경우든 escape 문을 통해 wildcard 문자를 일반 문자로 처리하도록 하여 find 명령에 전달시켜야 shell에 의해서 해석되지 않고 find 명령에 잘 전달된다. 따라서, 검색 문자열에 큰타옴표를 붙이거나 그 앞에 역슬래시(\)를 붙여야 한다.
find . -name  \*.java
find 명령을 수행할 때 검색할 위치를 지정하지 않은 경우에는 현재 디렉토리에서 검색이 시작된다.

아래 3개의 find 명령어를 실행하면 똑같이 현재 디렉토리와 하위 디렉토리, 숨겨진 파일을 모두 출력한다.
find find .find . -print
백업을 위해 검색 결과물에 전체 경로가 포함되기를 원한다면 find의 인자로 디렉토리를 입력할 때 전체 경로(absolute path)를 지정하면 된다.
find /home/bluher -name \*.java/home/bluher/plsql/REGEXPvalidate/src/oracle/otnsamples/plsql/ConnectionManager.java/home/bluher/plsql/REGEXPvalidate/src/oracle/otnsamples/plsql/DBManager.java/...
또한, 검색 문자열에 1개 이상의 시작 디렉토리를 지정할 수도 있습니다. 적정 권한을 가진 사용자로서 실행되는 경우, 아래 명령어는 모든 jar 파일을 찾기 위해 /usr, /home 및 /tmp 디렉토리 순서로 내려갈 것입니다:  
find /usr /home  /tmp -name "*.jar"
적절한 권한이 없는 사용자가 find 명령을 수행한 경우에는 다음과 같은 오류 메시지가 나올 수 있다:
find:  /tmp/orbit-root: Permission denied
다음과 같이 stderr(standard error)를 /dev/null로 보내도록 추가함으로써 오류 메시지가 출력되는 것을 제거할 수 있다.
find /usr /home  /tmp -name "*.jar" 2>/dev/null
기본적으로 find는 대/소문자를 구별한다. 대/소문자가 구별되는 find 버전을 사용할 경우, 대/소문자에 무관한 결과를 얻고자 할 경우에는 -name 옵션 대신 -iname 옵션을 사용하면 원하는 결과를 얻을 수 있다.
find downloads  -iname "*.gif"downloads/.xvpics/Calendar05_enlarged.gifdownloads/lcmgcfexsmall.GIF
또한, 파일명 외에도 유형(type)에 따라 파일을 검색할 수 있다. 다음 예는 현재 디렉토리의 모든 하위디렉토리를 검색한다.
find . -type d          
다음의 예는 /usr 디렉토리에 존재하는 모든 심볼릭 링크(symbolic link)를 찾고자 할 때 유용하게 사용될 수 있다.
find /usr -type l
root로 다음의 두 명령을 수행하면, /usr/bin 디렉토리에서 z로 시작하는 모든 파일들에 대한 링크와 링크가 가리키는 파일이 나타난다.
# find /usr/bin  -type l  -name "z*" -exec ls  -l {} \;lrwxrwxrwx 1 root  root 8 Dec 12 23:17 /usr/bin/zsh -> /bin/zshlrwxrwxrwx 1 root  root 5 Dec 12 23:17 /usr/bin/zless -> zmorelrwxrwxrwx 1 root  root 9 Dec 12 23:17 /usr/bin/zcat -> /bin/zcat
find /usr/bin -type  l  -name "z*" -ls
두 번째 명령의 경우에는 디렉토리와 inode 정보를 보여준다. -exec 및 –ls 실행은 뒤에서 다시 다뤄질 것이다.

find가 찾을 수 있는 다른 파일 유형으로는 다음과 같은 것들이 있다
   • b—block (buffered) special
   • c—character (unbuffered) special
   • p—named pipe (FIFO)
   • s—socket
   • f - normal file
   • d - directory
검색의 시작을 루트부터 하면 시스템 속도가 크게 느려질 수 있다. 명령어를 반드시 실행해야 한다면 사용량이 적은 시간대나 야간에 실행하는 것이 좋다.
find  /   -print > masterfilelist.out
여러 파일시스템을 마운트해서 사용하여 다량의 파일이 존재하는 Enterprise systems에서는 find 명령을 수행할 때는 여러 옵션들을 사용함으로써 시스템에 끼칠 수 있는 부하를 줄이는 것이 좋다. 이를 위해 가장 유용하게 사용할 수 있는 옵션은 –xdev와 -mount다.
이들 옵션은 MS-DOS, CD-ROM 또는 AFS와 같은 다른 파일 시스템 상의 디렉토리를 검색하지 않게 함으로써 검색의 범위를 좁혀준다. 그러므로 find 명령의 시작 디렉토리와 동일한 유형의 파일 시스템으로 검색을 제한할 수 있다.

듀얼 부팅 시스템의 경우 mount 명령어를 이용해서 다른 파일시스템을 Linux/Unix에서 마운트해서 사용하는 경우라면 이러한 옵션을 사용하여 부하를 줄일 수 있다. Windows 파티션이 관련되어 있다고 가정할 때 다음과 같이 마운팅할 수 있다.:
mount -t vfat  /dev/sda1 /mnt/msdos
df를 실행하거나 옵션없이 mount 명령만을 수행함으로써 파티션 마운팅 여부를 확인할 수 있다:
find /mnt/msdos  -name "*.txt" 2> /dev/null
MS Windows 파티션에서는 파일 목록이 길 수 밖에 없는데, -mount 또는 -xdev 옵션을 이용해 다음과 같이 명령을 수행해보라.
find / -name  "*.txt" -mount 2> /dev/null
또는
find / -name  "*.txt" -xdev 2> /dev/null
아래 예에서와 같이 -fstype 옵션을 이용해 찾고자 하는 파일 시스템을 정확하게 알려줄 수도 있다.
find / -name  "*.txt" -fstype vfat 2> /dev/null



시간 찾기

시스템의 타임 스탬프를 토대로 검색을 사용할 수 있는 여러 개의 옵션을 가지고 있다. 타임 스탬프로 사용할 수 있는 옵션으로는 다음과 같은 것들이 있다.
   • mtime파일 내용이 마지막으로 수정된 시간(modified time)
   • atime—파일을 읽기 또는 액세스한 시간(accessed time)
   • ctime—파일 상태가 변경된 시간(changed time)

mtime 및 atime의 의미는 그 자체로 쉽게 알 수 있지만, ctime는 좀 더 설명이 필요다.
inode는 각 파일에 대한 메타 데이터를 가지고 있기 때문에 파일이 변경되면 inode 데이터도 변경된다. 파일에 대한 심볼릭 링크 생성, 파일에 대한 권한 변경, 파일 이동 등과 같은 다양한 작업은 파일의 메타데이터를 변경시키며, 이러한 경우 inode가 변경된다. 파일 내용에 대한 읽기나 수정은 수행되지 않기 때문에  mtime 및 atime은 변하지 않지만 ctime은 변하게 된다. 이러한 시간 옵션은 각기 -n, n 또는 +n로 지정된 n 값을 함께 사용해야 한다.
• -n은 n 이하를 반환
• +n은 n 이상을 반환
• n은 정확히 n에 해당되는 값을 반환
보다 명확한 설명을 위해 몇 가지 예를 보자. 다음의 명령은 최근 1시간 동안 수정된 모든 파일을 찾아준다.
find . -mtime -1
./plsql/FORALLSample
./plsql/RegExpDNASample
/plsql/RegExpSample
-1 대신 1을 사용하면 정확하게 1시간 전에 수정된 모든 파일을 찾을 수 있다:
find . -mtime 1 
위의 예는 정확하게 일치하는 값을 대상으로 검색하기 때문에 모든 결과값이 나오지는 않을 수 있다. 다음 예는 1시간 이전에 수정된 파일을 찾아준다:
find . -mtime +1 
기본적으로 -mtime, -atime 및 –ctime는 최근 24 시간에 대한 타임 스탬프를 의미한다. 하지만, 뒤에 daystart 옵션을 붙이면 오늘을 시작으로 24시간 주기가 시작된다. 또한, mmin, amin 및 cmin을 사용하면 분 단위로 변경된 타임 스탬프를 찾을 수 있다.
로그인한 후 즉시 다음의 명령을 실행하면 최근 1분 내에 읽은 모든 파일을 찾을 수 있다:
find . -amin -1
./
./.bashrc
./.bash_history
./.xauthj5FCx1
find 명령으로 파일을 찾으면 메타 데이터의 일부인 파일의 액세스 시간(accessed time)이 변경된다는 사실에 주의해야 한다.

-newer, -anewer 및 –cnewer 옵션을 사용하면 특정 파일과 비교해 수정 또는 액세스된 파일을 찾을 수도 있다.
 이는 -mtime, -atime 및 –ctime과 비슷합니다.

• -newer : 보다 최근에 내용이 수정된 파일
• -anewer : 보다 최근에 읽기가 수행된 파일
• -cnewer : 보다 최근에 상태가 변경된 파일

마지막으로 생성한 tar.gz 파일 이후로 현재 디렉토리에서 어떤한 방법으로든지 수정된 파일을 모두 찾으려면 다음과 같이 명령을 수행하면 된다.
find . -newer  backup.tar.gz


크기에 따른 파일 찾기

-size 옵션은 지정된 크기 기준에 부합하는 파일을 찾아준다. 크기가 5MB 이상인 모든 파일을 찾으려면 다음과 같이 명령을 수행하면 된다.
find / -size  +5000000c 2> /dev/null
/var/log/lastlog
/var/log/cups/access_log.4
/var/spool/mail/bluher
마지막에 “c”를 붙이면 결과가 바이트 단위로 보고된다.(기본적으로 find 명령은 512 바이트 블록의 수로 크기를 보고한다.)
또한, “c”를 “k”로 교체하는 경우에는 킬로바이트 수로, “w”를 사용하는 경우에는 2바이트 워드의 수로 결과를 표시할 수 있다.

-size 옵션은 모든 제로 바이트 파일을 찾거나 이들 파일을 /tmp/zerobyte 폴더로 이동하기 위해 자주 사용된다.
다음 예는 바로 이러한 연산을 수행한다.
find test -type f  -size 0 -exec mv {} /tmp/zerobyte \;
-exec 옵션은 find 명령이 만나게 되는 파일에 대해 모든 쉘 명령어를 수행할 수 있도록 해준다.
후반부에서 이 옵션의 보다 다양한 사용 예를 확인할 수 있다. 중괄호를 사용하면 빈 파일 각각을 이동시킬 수 있다.
또한, -empty 옵션을 사용하면 빈 파일을 찾을 수 있다.
find test -empty        
test/foo
test/test


권한 및 소유권에 따른 파일 찾기

find 명령은 시스템 보안 모니터링을 위해 없어서는 안될 명령이다. 아래와 같이 기호법이나 8진법을 통해 사용 권한이 모두에게 열려있는 파일을 찾을 수 있다.
find . -type f  -perm a=rwx -exec ls -l {} \; 
또는
find . -type f  -perm 777 -exec ls -l {} \;
-rwxrwxrwx 1 bluher  users 0 May 24 14:14 ./test.txt
위의 두 명령의 경우 -exec ls –l을 수행하고 있기 때문에, 반환된 파일의 실제 사용 권한을 확인할 수 있으며, 위의 예는 “기타 사용자” 및 그룹이 모두 쓰기를 수행할 수 있는 파일을 찾아준다.
find plsql -type f  -perm -ug=rw -exec ls -l {} \; 2>/dev/null
또는
find plsql -type f  -perm -220 -exec ls -l {} \; 2>/dev/null 
-rw-rw-rw- 1 bluher users 4303  Jun  7   2004 plsql/FORALLSample/doc/otn_new.css
-rw-rw-rw- 1 bluher users 10286 Jan  12  2005  plsql/FORALLSample/doc/readme.html
-rw-rw-rw- 1 bluher users 22647 Jan  12  2005  plsql/FORALLSample/src/config.sql
..
아래 명령어들을 실행하면 사용자, 그룹 또는 둘 모두가 쓰기 작업을 할 수 있는 파일을 찾을 수 있다.
find plsql -type f  -perm /ug=rw -exec ls -l {} \; 2>/dev/null
또는
find plsql -type f  -perm /220 -exec ls -l {} \; 2>/dev/null
-rw-r--r-- 1 bluher users 21473  May  3 16:02 plsql/regexpvalidate.zip
-rw-rw-rw- 1 bluher users 4303  Jun  7   2004 plsql/FORALLSample/doc/otn_new.css
-rw-rw-rw- 1 bluher users 10286 Jan  12  2005  plsql/FORALLSample/doc/readme.html
-rw-rw-rw- 1 bluher users 22647 Jan  12  2005  plsql/FORALLSample/src/config.sql
웹이나 기존 매뉴얼에는 아래 명령어로 나와 있을 것입니다.
find . -perm +220  -exec ls -l {} \; 2> /dev/null 
+ 기호는 / 기호와 동일한 역할을 하지만, 새로운 버전의 GNU findutils에서는 권장되지 않고 있다.
시스템에서 쓰기 가능한 모든 파일을 찾으려면 아래와 같이 명령을 사용하면 된다.
find / -wholename  '/proc' -prune  -o  -type f -perm -0002 -exec ls -l {} \;
-rw-rw-rw- 1 bluher users 4303  Jun  7   2004/home/bluher/plsql/FORALLSample/doc/otn_new.css
-rw-rw-rw- 1 bluher users 10286 Jan  12  2005  /home/bluher/plsql/FORALLSample/doc/readme.html
...
4번째 사용 권한에 대해 앞으로 조금 더 다루겠지만, 마지막 필드의 “2”는 쓰기 비트로도 알려져 있는 파일 사용 권한의 “기타 사용자”에 해당된다. 우리는 설정된 다른 사용 권한이 무엇이든 관계 없이 기타 사용자를 위해 설정된 쓰기 권한을 가진 파일을 보고 싶다는 표시로 0002라는 사용 권한 모드 앞에 대시를 사용했습니다.
위의 명령어에서는 3가지 새로운 개념이 도입되었습니다. 패턴이 발견된 경우, – prune은 파일 패턴 “/proc”에서 -wholename 테스트를 사용함으로써 find 명령어가 이 디렉토리로 내려오지 않도록 막아줍니다. 불린 연산자 “-o”를 통해 find 명령어는 다른 디렉토리에서 명령어의 나머지 부분을 처리할 수 있습니다. 각 표현식 간에 가정된 암시적 and 연산자(-a)가 있기 때문에 좌측 표현식이 거짓(false)으로 평가된 경우 and 뒤에 나오는 표현식은 평가되지 않습니다. 따라서, -o 연산자가 필요합니다. 강제 우선 순위 적용을 위해 괄호를 사용하는 것처럼 find 명령어는 불린 연산자 -not, !,도 지원합니다.
시스템 관리자는 자주 find 명령어를 통해 해당 사용자나 그룹의 이름 또는 ID를 사용해 특정 사용자나 그룹의 정규 파일을 검색하고 있습니다:
[root] $  find / -type f -user bluher -exec ls -ls {}  \;
여기, 이러한 명령어의 출력에 대한 간단한 예제가 나와 있습니다:
4 -rw-r--r-- 1 bluher users 48  May  1 03:09  /home/bluher/public_html/.directory
4 -rw-r--r-- 1 bluher users 925  May  1 03:09 /home/bluher/.profile
또한, find 명령어를 사용해 그룹 별로 파일을 검색할 수도 있습니다:
[root] $ find /  -type f -group users
find / -type d -gid  100
이 명령어를 실행하면 그룹 ID 100이 소유하고 있는 디렉토리 목록이 나옵니다. 해당되는 uid 또는 gid를 찾기 위해 /etc/passwd 또는 /etc/group 파일에서 more 또는 cat 명령어를 실행할 수 있습니다
이 명령어는 알려진 특정 사용자 및 그룹에 관한 파일을 찾는 것 외에도 사용자나 그룹이 지정되지 않은 파일을 찾는 데도 유용합니다. 아래 명령어는 /etc/passwd 또는 /etc/group 파일에서 리스트를 가지고 있지 않은 파일을 식별합니다.
find / -nouser -o  -nogroup
위 명령어는 실제로 시스템 상에 결과를 제공하는 것은 아니지만, 파일을 이동한 후에 사용자나 그룹이 지정되지 않은 파일을 식별하는 데 사용할 수 있습니다
이제, 본 섹션 초반부에 언급한 별도의 높은 사용 권한 문제를 해결할 수 있게 되었습니다.
SGID 및 SUID는 UNIX 기반 운영 체제 상의 파일 및 디렉토리에 할당할 수 있는 특별 액세스 권한 플래그입니다. 컴퓨터 시스템 액세스에 있어 일반 권한을 가진 사용자가 일시적으로 높아진 권한을 가지고 바이너리 실행 파일을 수행할 수 있습니다.
find /  \( -perm -2000 -o -perm -4000 \) -ls
167901   12 -rwsr-xr-x   1 root     root         9340 Jun 16  2006 /usr/bin/rsh
167334   12 -rwxr-sr-x   1 root     tty         10532 May  4  2007 /usr/bin/wall
위 명령어에서 괄호가 escape 처리된 것을 볼 수 있습니다. 또한, 권한 간의 차이점을 확인할 수 있습니다. 첫 번째 파일에는 SGID 권한이 설정되어 있으며 두 번째 파일에는 SUID 권한이 설정되어 있습니다. 위 명령어의 마지막 연산은 -exec ls -dils 연산의 find 명령어와 유사합니다

find 명령어 제어

Linux의 많은 명령어와 달리, find 명령어는 -r 또는 -R 옵션이 없이도 서브디렉토리로 내려가지 않습니다. 이는 기본적으로 지원됩니다. 하지만, 때에 따라 이를 제한하고 싶을 수 있습니다. 이럴 경우, -depth, -maxdepth 및 –mindepth 옵션과 –prune 연산이 유용합니다
-prune이 얼마나 유용한지는 이미 확인했기 때문에 이제는 -depth, -maxdepth 및 -mindepth 옵션에 대해 살펴보겠습니다.
-maxdepth 및 -mindepth 옵션을 사용하면 find 명령어를 통해 검색하고자 하는 디렉토리 트리의 수준을 지정할 수 있습니다. find 명령어가 단 한 수준의 디렉토리를 찾도록 하고 싶은 경우에는 maxdepth 옵션을 사용할 수 있습니다.
아래 명령어를 실행해 디렉토리 트리의 상위 3개 수준의 로그 파일을 검색하면 –maxdepth의 효과를 확인할 수 있습니다. 이렇게 하면 –maxdepth 없이 실행할 때 보다는 훨씬 적은 출력 결과가 나옵니다
find / -maxdepth 3  -name "*log"
또한, find 명령어에게 디렉토리 트리에서 최소 3개 수준까지 디렉토리를 검색할 것을 지시할 수 있습니다.
find / -mindepth 3  -name "*log"
-depth 옵션은 내용 평가에 앞서 디렉토리를 평가할 수 있도록 해줍니다. 아래 명령어는 한 예입니다.:
find -name "*test*" -depth
./test/test
./test
./localbin/test
./localbin/test_shell_var
./localbin/test.txt
./test2/test/test
./test2/test
./test2

댓글 없음:

댓글 쓰기