[완료] find에서 -prune 옵션에 대해서..

nomail의 이미지

find의 맨페이지를 보면 -prune 옵션에 대해 나오는데
지정한 이름이 디렉토리 일때 그 이하 서브디렉토리는 탐색하지 않는다는 설명이 있습니다.
그런데 제가 잘못 이해한건지.. 테스트를 해보면 의도한대로 동작하지 않습니다.

cd /etc
find . -name 'config*'
 
./pcmcia/config.opts
./i3/config
./i3/config.keycodes
./vmware/hostd/config.xml
./vmware/config

위 결과에서 i3 디렉토리를 제외하고 검색 하고자 했을 때 다음 코드를 실행하면,

find -name 'config*' \( -path '*i3*' -prune \)
 
./i3/config
./i3/config.keycodes

오히려 반대의 결과가 나옵니다. 맨페이지 내용이랑 결과가 다른데.. 왜 이렇게 나오는 건가요?

익명 사용자의 이미지

find -name 'config*' \( -path '*i3*' -prune \)

는 bash 문법에 어긋나는 명령어 같습니다.

dontdieych의 이미지

-name 'config*' 조건에서 디렉토리가 일단 걸러집니다. config로 시작하는 이름의 디렉토리가 없습니다.

./pcmcia/config.opts
./i3/config
./i3/config.keycodes
./vmware/hostd/config.xml
./vmware/config

이 결과는 다 파일 입니다. 디렉토리가 아닙니다.

-path 'i3' 조건에서 두개만 남습니다.

./i3/config
./i3/config.keycodes

두 파일은 디렉토리가 아니기 때문에 -prune은 아무일도 하지 않고 '참'을 반환 합니다.

If the expression contains no actions other than -prune, -print is performed on all files for which the expression is true.

맨페이지의 이 설명대로라면 이렇게 됩니다.

find \( -name 'config*' \( -path '*i3*' -prune \) \) -print

앞서 살펴본 것 처머 이 표현식에서는 -prune에 디렉토리가 전달되지 않았기 때문에 하는일이 실질적으로 없습니다.

find -name 'config*' -path '*i3*'

이 명령과 완전히 같은 명령이 됩니다. -name 'config*'의 결과 중에서 디렉토리가 있었다면 -prune이 실제로 뭔가 하는 일이 생겼을 겁니다. 출력 결과에서 그 디렉토리 이하의 파일들은 걸러지겠죠. 그렇다고 해도 ./i3 디렉토리를 결과에서 제외할려는 원래의 목적은 달성하지 못합니다.

특정 결과를 제외하기 위해서 간단하게는 이렇게 쓸 수 있을 겁니다.

find -name 'config*' -not -path '*i3*'

좀 더 분명한 의미로 써 보면,

find . -not -path './i3*' -and -name 'config*' -and -print

-and와 -print는 암묵적으로 적용되는 것이니,

find . -not -path '.i3*' -name 'config*'

이렇게 쓸 수 있겠지요.

-prune 저도 이 글 보고 읽어 봤더니 어떻게 쓰라는 건지 이해하기가 참 애매하더군요. 일단 -prune이 조건이 아니고 액션이라는 사실을 잊지 말아야 합니다. -print, -exec 같은 액션 입니다. 액션을 하고 참을 반환한다. 이게 중요 합니다.

그리고 -prune이 하는 일은 디렉토리를 만났을 때 find가 그 디렉토리로 들어가지 못하게 하는 겁니다. find가 손에 쥐고 조건 검사를 하려는 그 파일 목록들중 일부를 쳐낼 수가 있습니다.

그래서 -prune이 효과적으로 쓰일 수 있는 경우는 불필요한 검사 횟수를 크게 줄일 수 있다는 점 같습니다.

➜ ~ time -p sudo find / -path '/usr' -or -path '/home' -or -path '/var'
/var
/usr
/home
real 25.92
user 3.10
sys 9.20
➜ ~ time -p sudo find / \( -path '/usr' -or -path '/home' -or -path '/var' \) -prune
/var
/usr
/home
real 0.56
user 0.25
sys 0.26

조금 억지스러워 보이지만 -prune의 효과를 확실히 보여주는 것 같습니다. /usr, /home, /var 이 세 디렉토리 만을 찾는 조건인데 첫번째 경우는 각 디렉토리의 모든 파일과 디렉토리에 대해서 계속 검사를 합니다. 두번째 경우는 같은 결과를 수행하지만 -prune 덕분에 검사 횟수가 현저히 줄어 듭니다. 제 컴퓨터에서 대충 25초 이상 차이 나네요.

그럼 반대로 /usr, /home, /var를 제외한 나머지 부분에서만 어떤 조건을 검사하고 싶다면 어떻게 할까요.

출력 결과를 좀 간단하게 할려고 임의의 디렉토리 구조를 만들어 봤습니다.

➜  pwd
/tmp/find
➜  tree
.
├── 0
├── 1
│   └── 1
├── 2
│   └── 2
└── 3
    └── 3

3 directories, 4 files
➜  find
.
./3
./3/3
./2
./2/2
./1
./1/1
./0

여기서 ./2 디렉토리를 제외한 모든 파일에 find 검색을 하고 싶다. 이게 우리 목적 입니다. ./2 디렉토리 아래에 파일이 한 수백만개 있다 그러면 이 때 -prune을 쓰는게 말이 됩니다.

➜  find -path ./2 -prune -or -name 1
./2
./1
./1/1

./2/2, 다시 말하면 ./2/* 는 제외 했는데 ./2는 여전히 나오네요. 그런데 이 때 명시적으로 -print를 써주면 ./2가 안나옵니다.

➜ find -path ./2 -prune -or -name 1 -print
./1
./1/1

아까 맨페이지에서 인용한 부분중에 액션이 -prune 뿐이면 -print가 자동적으로 붙는다고 했습니다. 이렇게 되겠죠.

➜ find \( -path ./2 -prune -or -name 1 \) -print
./2
./1
./1/1

-prune은 액션을 하고 참을 반환하기 때문에 ./2도 -print에 걸려 듭니다.

갈수록 복잡해지는데 헷갈림 없이 -prune을 쓰려면 다음 패턴을 기억해야 할 것 같습니다.

find  [exp A] -prune -or [exp B] -action

제일 뒤에 있는 -action을 생략하면 암묵적으로 전체 표현식에 -print가 적용 되므로 -prune이 발동된 그 디렉토리도 출력 결과에 포함 된다는 사실을 잊지 마세요.

아니면 우리가 find를 쓸 때 이렇게 많이 씁니다.

find -조건1 -조건2 -조건3

이렇게 해서 결과를 출력하죠 보통 -print는 생략하구요. 이 때 결과 중에서 디렉토리 통채로 제거할 경우, '-print -or -path ./path1 -prune' 이렇게 붙여 줍니다.

find -조건1 -조건2 -조건3 -print -or -path ./path1 -prune

-print를 빼먹으면 ./path1도 결과에 출력 된다는 사실을 잊지 맙시다. 찡긋.

이 글을 학습, 번역 하였습니다.

http://stackoverflow.com/questions/1489277/how-to-use-prune-option-of-find-in-sh

➜ ~ time -p sudo find / -name doc | wc -l
real 24.70
user 2.34
sys 9.01
4411
➜ ~ time -p sudo find / -not \( -path '/usr*' -or -path '/home*' -or -path '/var*' \) -name doc | wc -l
real 27.11
user 2.65
sys 9.56
0
➜ ~ time -p sudo find / -name doc -print -or \( -path /usr -or -path /home -or -path /var \) -prune | wc -l
real 0.73
user 0.38
sys 0.29
0
➜ ~
nomail의 이미지

긴글 답변 주셔서 정말 감사드립니다.
코드랑 같이 적는게 쉽지가 않는데 정말 무한한 정성이 느껴집니다.
이렇게 부연설명이 필요한데.. 왜 맨페이지에는 대충 적어놨을까요ㅜㅜ
덧글 설명을 3번 정독하고 완전히 이해했구요. 제 시스템에서 여러번 테스트 해보니 확실히 사용법을 알겠습니다.

-prune 옵션의 핵심은, 조건이 아니라 액션이라는 것과

If the expression contains no actions other than -prune, -print is performed on all files for which the expression is true.

이 부분이네요.

그리고 find -or 조건식이 제가 이해하는 or의 개념이 아니라 좀 헷갈렸는데 가령,

expr1 -o expr2
    Or; expr2 is not evaluated if expr1 is true.

expr1이 true 이면 expr2는 무시한다는 의미인데, 그렇다면

find -조건1 -조건2 -조건3 -print -or -path ./path1 -prune

위에서 -or 앞쪽의 print는 모두 true 이니까, 뒤쪽 prune은 실행이 안될거라고 생각했습니다.
곰곰히 생각해보니 이미 디렉토리를 읽었을 때 -or 앞쪽 -name 조건식이 false가 되고 prune 액션 후, 그 디렉토리는 걸러지는 것 같네요.

상세한 설명 덕분에 find 명령을 확실히 배웠습니다. 정말 감사드립니다^^

dontdieych의 이미지

info find 하시면 좀 상세한 문서를 보실 수 있습니다. KDE나 GNOME 도움말 브라우저로 보시면 좀 편합니다.

find -조건1 -조건2 -조건3 -print
               -or
     -path ./path1 -prune

이렇게 보면 좀 더 머릿속으로 그리기가 편할 것 같습니다. -path로 들어오는 파일은 이미 조건 3가지 중 하나에서 탈락한 파일 입니다.

nomail의 이미지

부연 설명도 감사합니다.
쉽게 설명해주셔서 파악하는데 많은 도움이 되었구요.
처음 맨페이지 봤을 때 복잡해 보였는데 옵션과 조건식, 액션의 개념구분만 있으면 좀 손 쉽게 사용할 수 있을 것 같습니다^^

kombo67의 이미지

흑.. 회사 UNIX 인데 info find 가 안먹히는군요 ㅜ
반복해서 읽었는데 이해가 잘 안가 글을 남깁니다..

ex) ./~.sh /var *.log 라 쳤을때 /var 에 있는 *.log 로 시작하는 것들을 묶으려고 합니다 그런데 하위로 계속 넘어가게 되어
-prune 을 쓰려고 하는데요

find . -name '*.log' \( -path '*tes*' -o -prune -print \)
하여서 성공은 했는데

drwxrwxr-x    5 root     staff          4096 Aug 21 17:25 .
drwxr-xr-x   22 root     system         4096 Jul 27 13:59 ..
-rw-------    1 root     system         2583 Sep  3 20:29 .bash_history
drwx------    2 root     system          256 Jul 24 09:00 .ssh
drwx------    2 root     system          256 Jul 24 08:57 .topasrecrc
-rw-------    1 root     system          122 Sep  4 10:10 .vi_history
-rw-------    1 root     system          349 Jul 27 17:08 dead.letter
-rw-r--r--    1 root     system            0 Aug 21 17:16 fin.log
-rwxr--r--    1 root     system          170 Sep  4 10:10 logzipdel.sh
-rw-------    1 root     system         3239 Jul 27 17:06 mbox
-rwxr--r--    1 root     system          147 Aug 21 17:30 re.sh
-rw-r--r--    1 root     system       105299 Aug 20 19:38 smit.log
drwxr-xr-x    2 root     system          256 Sep  4 13:41 tes

디렉은 이렇습니다만 제가 해석하기에 위와 같이 명령어를 치면 .ssh 는 나와야할법 한데... 죄송합니다 이해가 덜 됬나봐요...
1. find 에서 ./(블라블라) < - 이런류의 파일만 받음
2. ./(블라블라)/(블라블라) X < - 즉 하위까지 검색 안함
3. 여기에서 (\ 구문을 썼는데 여기서의 결과가 \( 앞의 -name '*.log' 에 들어가게 되는 건가요...?

도움을 요청..합니다..ㅜㅜ

dontdieych의 이미지

한 줄 요약:

find -name 'config*' -print -or -path './i3' -prune
khiny의 이미지

요새 저도 -prune과 -or 때문에 헷갈려서 이리저리 검색 중이였는데
상세하고 자세한 설명덕분에 이해할 수 있게됐습니다~

dontdieych의 이미지

find 하다가 man find 보다가 뭔가 애매하게 궁금해서 구글 검색 하다가 들어 왔는데 제가 8년 전 즈음에 쓴 글이 저한테 답을 주네요.

글타래 읽다 보니 제 아바타가 보이길래 뭐지? 했는데 제가 쓴 답글이 있네요.

사람의 기억력이란 정말 믿지 말아야 할 것 같습니다. 8년 전에 저렇게 길고 장황하게 -prune 에 대해서 썼는데 오늘 기억 안나서 다시 검색해보는 날 이네요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.