find
find 명령은 파일을 검색하는데 사용할 수 있는 많은 옵션들을 제공합니다. 필요로 하는 옵션에 대해서는 man 페이지를 참조하시고 여기서는 기본적인 find 명령의 사용법에 대해서 알아보겠습니다.
앞으로 설명을 위한 명령 실행에는 다음과 같은 디렉토리 구조를 대상으로 하겠습니다.
$ find
.
./dir_2
./dir_2/bar_1.html
./dir_2/dir_3
./dir_2/dir_3/bar_2.txt
./dir_2/dir_3/bar_2.html
./dir_2/bar_1.txt
./.hidden.txt
./index.txt
./index.html
./.git
./.git/git.txt
./.git/git.html
./dir_1
./dir_1/foo.html
./dir_1/foo.txt
위의 출력에서 보는 것과 같이 find 명령은 아무런 인수를 주지 않으면 find .
과 같습니다.
따라서 출력의 앞부분에 ./
가 포함되는데 이것이 필요하지 않을 경우 다음과 같이 하면 됩니다.
# 디렉토리, 파일명을 직접 적어준다.
$ find dir_1
dir_1
dir_1/foo.html
dir_1/foo.txt
# 또는 디렉토리 전체를 검색하려면 다음과 같이 shell globbing 을 사용하면 됩니다.
# "*" glob 문자는 기본적으로 "." 으로 시작하는 hidden 파일은 매칭에 포함되지 않기 때문에
# .git 디렉토리 내용은 포함되지 않는 것을 볼 수 있습니다.
$ find *
dir_1
dir_1/foo.html
dir_1/foo.txt
dir_2
dir_2/bar_1.html
dir_2/dir_3
dir_2/dir_3/bar_2.txt
dir_2/dir_3/bar_2.html
dir_2/bar_1.txt
index.html
index.txt
# "." 로 시작하는 파일도 매칭에 포함하려면 다음과 같이 합니다.
$ find -printf '%P\n'
# 또는
$ find * .[^.]*
. . .
. . .
dir_2/dir_3/bar_2.html
dir_2/bar_1.txt
index.html
index.txt
.git
.git/git.txt
.git/git.html
.hidden.txt
find 명령은 표현식( expression ) 으로 구성됩니다.
여기에는 tests, actions, operators 등이 있습니다.
-maxdepth
-mindepth
같은 것은 따로 global options 라고 합니다.
tests
-name '*.sh'
, -type f
, -mtime -1
, -size +1M
. . . 같은 표현식이 test 에 해당합니다.
actions
-print
, -prune
, -delete
, -exec
, -ls
, -quit
. . . 등은 action 에 해당합니다.
operators
우선순위가 높은 순서부터입니다.
( . . . )
-not
,!
-and
,-or
또는-a
,-o
( 우선순위는 -and 가 높습니다 )
tests 와 actions 은 참, 거짓을 반환하므로 -and
, -or
연산자를 이용해 연결할 수 있습니다.
# index.html 파일에 대해 -type d 테스트는 거짓이므로 값이 표시되지 않는다.
$ find index.html -type d -and -name '*.html'
$ find index.html -type f -and -name '*.html' # -type f 로 변경
index.html
# index.html 파일에 대해 -exec test -d {} \; 액션은 비정상 종료 상태 값을 반환하므로
# 뒤이어지는 -exec echo {} \; 는 실행되지 않는다.
$ find index.html -exec test -d {} \; -and -exec echo {} \;
$ find index.html -exec test -f {} \; -and -exec echo {} \; # test -f 로 변경
index.html
기본적으로 -and
연산자는 생략해서 사용할 수 있습니다.
$ find index.html -type f -name '*.html'
index.html
$ find index.html -type d -name '*.html' # -type d
$
# -or 연산자는 생략할 수 없습니다.
$ find index.html -type d -o -name '*.html'
index.html
test 에는 하나 이상의 action 을 붙여 사용할 수 있습니다.
test 에는 출력을 위해 기본적으로 -print
액션이 적용됩니다.
따라서 다음 두개로 구성된 명령들은 서로 같은 것입니다.
$ find * -name '*.html'
$ find * -name '*.html' -print
$ find index.html -type d -o -name '*.html'
index.html
$ find index.html -type d -print -o -name '*.html' -print
index.html
$ find -name .git -prune
./.git
$ find -name .git -prune -print
./.git
하지만 다음과 같이 test 에 직접 -prune
을 제외한 action 이 사용되면
다른 test 에는 디폴트로 -print 가 적용되지 않게 됩니다.
# -name '*.txt' 테스트에 직접 -print 액션이 사용되어
# -name '*.html' 테스트에는 -print 가 디폴트로 적용되지 않는다.
$ find * -name '*.html' -o -name '*.txt' -print
dir_1/foo.txt
dir_2/dir_3/bar_2.txt
dir_2/bar_1.txt
index.txt
# -name '*.txt' 에는 -print 가 적용되지 않는다.
$ find * -name '*.html' -print -o -name '*.txt'
dir_1/foo.html
dir_2/bar_1.html
dir_2/dir_3/bar_2.html
index.html
# find * -name '*.html' -o -name '*.txt' 와 같은 것임
$ find * -name '*.html' -print -o -name '*.txt' -print
dir_1/foo.html
dir_1/foo.txt
dir_2/bar_1.html
dir_2/dir_3/bar_2.txt
dir_2/dir_3/bar_2.html
dir_2/bar_1.txt
index.html
index.txt
-prune
액션은 특정 디렉토리를 검색에서 잘라낼 때 사용합니다.
매칭이 되었을 때 뒤이어지는 표현식이 실행되면 안되므로 -or
연산자를 사용합니다.
명령을 작성할 때 주의할 점은 다음 첫번째 명령과 같이 작성하게 되면
이것은 두번째 명령과 같게되므로 출력에 ./.git
이 남게 됩니다.
따라서 세번째와 같이 작성해야 ./.git
이 포함되는 것을 방지할 수 있습니다.
$ find -name .git -prune -o -name '*.html'
./dir_2/bar_1.html
./dir_2/dir_3/bar_2.html
./index.html
./.git <---- ./.git 이 포함된다.
./dir_1/foo.html
# 이것은 위 명령과 같은 것임
$ find -name .git -prune -print -o -name '*.html' -print
# 다음과 같이 작성해야 -prune -print 가 되지 않는다.
$ find -name .git -prune -o -name '*.html' -print
./dir_2/bar_1.html
./dir_2/dir_3/bar_2.html
./index.html <---- ./.git 이 제거 되었다.
./dir_1/foo.html
( . . . ) 연산자의 사용
( )
연산자는 우선순위를 조절하는 용도 외에 중복방지용으로도 사용됩니다.
$ find -name .git -prune -o -name dir_1 -prune -o -name '*.html' -print
./dir_2/bar_1.html
./dir_2/dir_3/bar_2.html
./index.html
# 중복되는 -prune 액션을 ( ) 을 이용해 한번만 사용할 수 있습니다.
$ find \( -name .git -o -name dir_1 \) -prune -o -name '*.html' -print
./dir_2/bar_1.html
./dir_2/dir_3/bar_2.html
./index.html
# -print0 액션을 두개의 -name 테스트에 모두 적용
$ find \( -name '*.txt' -o -name '*.html' \) -print0
$ find \( -name '*.txt' -o -name '*.html' \) -printf '%P\0' # \0 NUL 문자
$ find \( -name '*.txt' -o -name '*.html' \) -printf '%P\n' # \n newline
time 테스트와 +n, -n, n 숫자 값의 사용
+n
은 greater thann
-n
은 less thann
n
은 exactlyn
-mtime
, -mmin
... 에 설정한 값은 기본적으로 현재 시간으로부터 계산을 합니다.
그러므로 -mmin -60
은 현재 시간으로부터 60 분 이내에 수정된 파일을 말하고
-mtime -1
는 현재 시간으로부터 1 일 이내 ( 24 시간 이내 ) 수정된 파일들을 말합니다.
따라서 현재 시간이 15 시 일경우 어제 18 시에 수정한 파일은 검색에 포함되지만
09 시에 수정한 파일은 포함되지 않게 됩니다.
만약에 시간 계산을 현재 시간으로부터가 아닌 from the beginning of today 부터
하려면 -daystart
옵션을 사용합니다.
다음은 현재 시각으로부터 수정한지 60 분 이내의 파일들을 검색하는 명령입니다.
# 1. -prune : dir_1 과 dir_3 는 잘라내고
# 2. -type f : 디렉토리는 제외하고 일반 파일만
# 3. -mmin -60 : 현재 시각으로부터 수정한지 60 분 이내의 파일만
$ find * \( -name dir_1 -o -name dir_3 \) -prune -o -type f -mmin -60 -exec ls {} \;
dir_2/bar_1.html
dir_2/bar_1.txt
index.html
index.txt
# 수정한지 60 분이 지난 파일들
-mmin +60
# 수정한지 3 일 이내의 파일들
-mtime -3
# 수정한지 3 일이 지난 파일들
-mtime +3
# 수정한지 3 일째 되는 파일들만
-mtime 2
# 수정한지 1 일 이내의 파일들
-mtime -1 과 -mtime 0 은 같은 결과
------------------------------------------------------------
$ date # 현재 11 일 12 시
Mon Jun 11 12:38:32 KST 2018
# -mtime -1 ( 현재 시간으로 부터 1 일 이내 파일 )
# 현재 시간으로부터 계산을 하므로 날짜가 Jun 10 인 파일도 보인다.
$ find * -type f -mtime -1 -ls
6572539 8 -rw-rw-r-- 1 mug896 mug896 7757 Jun 10 13:36 eval.md
6572529 12 -rw-rw-r-- 1 mug896 mug896 8212 Jun 11 12:42 find.md
6571730 16 -rw-rw-r-- 1 mug896 mug896 15779 Jun 10 13:37 here_document.md
6572864 4 -rw-rw-r-- 1 mug896 mug896 2703 Jun 11 08:28 SUMMARY.md
6572862 12 -rw-rw-r-- 1 mug896 mug896 8902 Jun 10 15:30 test_operators.md
# -mtime 0 ( 현재 시간으로부터 1 일째 되는 파일. 위와 같은 결과 )
$ find * -type f -mtime 0 -ls
6572539 8 -rw-rw-r-- 1 mug896 mug896 7757 Jun 10 13:36 eval.md
6572529 12 -rw-rw-r-- 1 mug896 mug896 8212 Jun 11 12:42 find.md
6571730 16 -rw-rw-r-- 1 mug896 mug896 15779 Jun 10 13:37 here_document.md
6572864 4 -rw-rw-r-- 1 mug896 mug896 2703 Jun 11 08:28 SUMMARY.md
6572862 12 -rw-rw-r-- 1 mug896 mug896 8902 Jun 10 15:30 test_operators.md
# -mtime -3 ( 현재 시간으로부터 3 일 이내 파일 )
# 뒤에 나오는 exit_status.md 파일은 같은 Jun 8 이지만 여기에 추가되지 않는다.
$ find * -type f -mtime -3 -ls
6572539 8 -rw-rw-r-- 1 mug896 mug896 7757 Jun 10 13:36 eval.md
6560999 12 -rw-rw-r-- 1 mug896 mug896 8458 Jun 11 12:47 find.md
6571730 16 -rw-rw-r-- 1 mug896 mug896 15779 Jun 10 13:37 here_document.md
6570224 8 -rw-rw-r-- 1 mug896 mug896 7430 Jun 8 18:25 keyword_commands.md <-- 추가
6572864 4 -rw-rw-r-- 1 mug896 mug896 2703 Jun 11 08:28 SUMMARY.md
6570196 12 -rw-rw-r-- 1 mug896 mug896 10343 Jun 8 18:20 test.md <-- 추가
6572862 12 -rw-rw-r-- 1 mug896 mug896 8902 Jun 10 15:30 test_operators.md
# -mtime -4 ( 현재 시간으로부터 4 일 이내 파일 )
$ find * -type f -mtime -4 -ls
6572539 8 -rw-rw-r-- 1 mug896 mug896 7757 Jun 10 13:36 eval.md
6572535 8 -rw-rw-r-- 1 mug896 mug896 7212 Jun 8 12:35 exit_status.md <-- 추가
6560999 12 -rw-rw-r-- 1 mug896 mug896 8458 Jun 11 12:47 find.md
6571730 16 -rw-rw-r-- 1 mug896 mug896 15779 Jun 10 13:37 here_document.md
6570224 8 -rw-rw-r-- 1 mug896 mug896 7430 Jun 8 18:25 keyword_commands.md
6572864 4 -rw-rw-r-- 1 mug896 mug896 2703 Jun 11 08:28 SUMMARY.md
6570196 12 -rw-rw-r-- 1 mug896 mug896 10343 Jun 8 18:20 test.md
6572862 12 -rw-rw-r-- 1 mug896 mug896 8902 Jun 10 15:30 test_operators.md
# -mtime 2 ( 현재 시간으로부터 3 일째 되는 파일 )
# exit_status.md 파일은 같은 Jun 8 이지만 여기에 포함되지 않는다.
$ find * -type f -mtime 2 -ls
6570224 8 -rw-rw-r-- 1 mug896 mug896 7430 Jun 8 18:25 keyword_commands.md
6570196 12 -rw-rw-r-- 1 mug896 mug896 10343 Jun 8 18:20 test.md
# -mtime 3 ( 현재 시간으로부터 4 일째 되는 파일 )
$ find * -type f -mtime 3 -ls
6572535 8 -rw-rw-r-- 1 mug896 mug896 7212 Jun 8 12:35 exit_status.md
-daystart
옵션을 사용하게 되면 날짜를 from the beginning of today 부터 계산을 하므로
파일의 날짜가 정확히 해당일에 매칭 되는 파일들이 출력됩니다.
$ date # 현재 11 일 12 시
Mon Jun 11 12:38:32 KST 2018
# -mtime -1 ( 1 일 이내 파일 )
# 정확히 날짜가 Jun 11 인 파일만 출력되고 Jun 10 인 파일들은 포함되지 않는다.
$ find * -daystart -type f -mtime -1 -ls
6560999 12 -rw-rw-r-- 1 mug896 mug896 9458 Jun 11 13:31 find.md
6572864 4 -rw-rw-r-- 1 mug896 mug896 2703 Jun 11 08:28 SUMMARY.md
# -mtime -2 ( 2 일 이내 파일 )
# 2 일 이내 이므로 Jun 10, 11 파일들이 출력된다.
$ find * -daystart -type f -mtime -2 -ls
6572539 8 -rw-rw-r-- 1 mug896 mug896 7757 Jun 10 13:36 eval.md
6560999 12 -rw-rw-r-- 1 mug896 mug896 9458 Jun 11 13:31 find.md
6571730 16 -rw-rw-r-- 1 mug896 mug896 15779 Jun 10 13:37 here_document.md
6572864 4 -rw-rw-r-- 1 mug896 mug896 2703 Jun 11 08:28 SUMMARY.md
6572862 12 -rw-rw-r-- 1 mug896 mug896 8902 Jun 10 15:30 test_operators.md
# -mtime 0 ( 1 일째 되는 파일 )
# 날짜가 Jun 10 인 파일들은 포함되지 않는다.
$ find * -daystart -type f -mtime 0 -ls
6560999 12 -rw-rw-r-- 1 mug896 mug896 9458 Jun 11 13:31 find.md
6572864 4 -rw-rw-r-- 1 mug896 mug896 2703 Jun 11 08:28 SUMMARY.md
# -mtime 1 ( 2 일째 되는 파일 )
# 정확히 날짜가 Jun 10 인 파일들만 출력된다.
$ find * -daystart -type f -mtime 1 -ls
6572539 8 -rw-rw-r-- 1 mug896 mug896 7757 Jun 10 13:36 eval.md
6571730 16 -rw-rw-r-- 1 mug896 mug896 15779 Jun 10 13:37 here_document.md
6572862 12 -rw-rw-r-- 1 mug896 mug896 8902 Jun 10 15:30 test_operators.md
# -mtime 3 ( 4 일째 되는 파일 )
# exit_status.md 파일을 포함해서 날짜가 Jun 8 인 파일들이 출력된다.
$ find * -daystart -type f -mtime 3 -ls
6572535 8 -rw-rw-r-- 1 mug896 mug896 7212 Jun 8 12:35 exit_status.md
6570224 8 -rw-rw-r-- 1 mug896 mug896 7430 Jun 8 18:25 keyword_commands.md
6570196 12 -rw-rw-r-- 1 mug896 mug896 10343 Jun 8 18:20 test.md
size 테스트와 +n, -n, n 숫자 값의 사용
+n
은 greater thann
-n
은 less thann
n
은 exactlyn
Suffix | Description |
---|---|
b | for 512-byte blocks (suffix 를 사용하지 않을 경우 default 값) |
c | for bytes |
w | for two-byte words |
k | for Kibibytes (KiB, units of 1024 bytes) |
M | for Mebibytes (MiB, units of 1024 * 1024 = 1048576 bytes) |
G | for Gibibytes (GiB, units of 1024 * 1024 * 1024 = 1073741824 bytes) |
greater than
# greater than 1 Kibibytes
$ find /bin -size +1k -exec stat -c "%s %n" {} + | sort -n | head -3
1297 /bin/bzmore
1659 /bin/cgroups-mount
1777 /bin/zcmp
# greater than 2 Kibibytes
$ find /bin -size +2k -exec stat -c "%s %n" {} + | sort -n | head -3
2131 /bin/zforce
2140 /bin/bzdiff
2301 /bin/gunzip
# greater than 1 Mebibytes
$ find /usr/bin -size +1M -exec stat -c "%s %n" {} + | sort -n | head -3
1052728 /usr/bin/pidgin
1094560 /usr/bin/gnome-keyring-daemon
1099184 /usr/bin/sqldiff
# greater than 2 Mebibytes
$ find /usr/bin -size +2M -exec stat -c "%s %n" {} + | sort -n | head -3
2248304 /usr/bin/hte
2283472 /usr/bin/qemu-arm
2291664 /usr/bin/qemu-armeb
less than
less than 은 사용 시 주의할 점이 하나 있는데 먼저 숫자 값에서 -1 을 해야 한다는 것입니다. 그러니까 1M 보다 작은 크기의 파일은 -1M 가 아니라 -2M 로 적어야 되고 5k 보다 작은 파일은 -5k 가 아니라 -6k 로 적어야 합니다.
# less than 0 Kibibytes 이 되므로 아무것도 표시되지 않는다.
$ find /bin -size -1k -exec stat -c "%s %n" {} + | sort -rn | head -3
$
# less than 1 Kibibytes
$ find /bin -size -2k -exec stat -c "%s %n" {} + | sort -rn | head -3
946 /bin/which
435 /bin/cgroups-umount
140 /bin/zfgrep
# less than 2 Kibibytes
$ find /bin -size -3k -exec stat -c "%s %n" {} + | sort -rn | head -3
2037 /bin/zless
1937 /bin/zcat
1910 /bin/zmore
# less than 0 Mebibytes 이 되므로 아무것도 표시되지 않는다.
$ find /bin -size -1M -exec stat -c "%s %n" {} + | sort -rn | head -3
$
# less than 1 Mebibytes
$ find /bin -size -2M -exec stat -c "%s %n" {} + | sort -rn | head -3
748968 /bin/brltty
584072 /bin/udevadm
554104 /bin/ip
# less than 2 Mebibytes
$ find /bin -size -3M -exec stat -c "%s %n" {} + | sort -rn | head -3
2022480 /bin/busybox
1113504 /bin/bash
748968 /bin/brltty
exact
suffix 를 사용하지 않을 경우 default 는 b
이므로 exact 사이즈는 c
suffix 를 사용합니다.
$ find /bin -size 101560c -ls
393495 100 -rwxr-xr-x 1 root root 101560 Apr 28 2017 /bin/gzip