Skip to content

find로 원하는 파일을 찾아보자! (2)

이번 시간에는 find표현식 중에서 연산자, 액션, 위치옵션이 무엇인지 알아보겠습니다.
시작하기 전에 지난 시간에 배운 find의 기본적인 사용법과 표현식에 대해 간단하게 복습해볼까요?
find는 파일을 탐색할 때 사용하는 명령어이며 find [옵션] [대상경로] [표현식] 의 형태로 사용합니다. 이 중 표현식은 파일을 탐색할 조건들을 의미합니다.
표현식의 종류는 4가지로 테스트, 연산자, 액션, 위치옵션이 있는데, 이러한 표현식들을 조합해서 원하는 파일을 탐색할 수 있습니다.
지난 글에서는 이 중 하나인 테스트에 대해서 공부했습니다.
테스트는 파일을 탐색하는 기준을 정의하는 역할을 하며 기준으로 정할 수 있는 항목은 파일명, 권한, 생성 및 변경시간 등이 있습니다.
이번 글에서는 테스트의 결과로 연산을 하는 연산자, 표현식의 결과를 활용해서 다른 명령어를 실행하는 액션 그리고 테스트의 수행 방법에 영향을 주는 위치옵션에 대해서 알아보겠습니다.
이번에 실습할 디렉터리의 구조는 아래와 같습니다.

$ tree ./
./
├── File
│   ├── File
│   ├── express.txt -> ../expression.txt
│   ├── file1.txt
│   ├── file2.txt
│   └── test.txt
├── Separator.txt
├── aa.txt
├── amin.txt
├── bb.txt
├── expression.tar.gz
├── expression.txt
├── findtestfile
├── grep-test
├── pattern
│   ├── findtestfile
│   ├── pattern1.txt
│   ├── pattern2.txt
│   └── pattern3.txt
└── test.txt


참고로 위와 같이 트리형태로 파일목록을 출력하고 싶을 때는 tree명령어를 사용하면 됩니다. 명령어를 찾을 수 없는 경우 yum, brew같은 패키지매니저를 통해 tree패키지를 설치해주면됩니다. e.g.) yum install tree, brew install tree ...

1. 연산자

연산자는 표현식의 결과를 연산할 때 사용합니다.
연산자의 종류로는 and, or, not 등이 있습니다.
논리연산자를 사용해본 경험이 있다면 각 연산자의 실행 결과를 쉽게 예상할 수 있을 것이라 생각됩니다.
그럼 각 연산자를 어떻게 사용하는지 알아보겠습니다.

1.1 AND

  • 표현식1 표현식2
  • 표현식1 -a 표현식2
  • 표현식1 -and 표현식2
$ find ./ -type f -name 'p*'
./pattern/pattern1.txt
./pattern/pattern2.txt
./pattern/pattern3.txt

$ find ./ -type f -a -name 'p*'
./pattern/pattern1.txt
./pattern/pattern2.txt
./pattern/pattern3.txt

$ find ./ -type f -and -name 'p*'
./pattern/pattern1.txt
./pattern/pattern2.txt
./pattern/pattern3.txt


AND연산은 위 처럼 3가지 형태로 수행할 수 있습니다.
논리연산자 AND(&)와 마찬가지로 표현식1, 2를 모두 만족하는 대상만 결과에 포함합니다.

1.2 OR

  • 표현식1 -o 표현식2
  • 표현식1 -or 표현식2
$ find ./ -type d -o -name 'a*'
./
./pattern
./aa.txt
./amin.txt
./File

$ find ./ -type d -or -name 'a*'
./
./pattern
./aa.txt
./amin.txt
./File


OR 연산은 위 2가지 형태로 사용합니다.
논리연산자 OR(|)와 마찬가지로 표현식1, 2중 하나만 만족하면 결과에 포함합니다.

1.3 NOT

  • ! 표현식
  • -not 표현식
$ find ./ ! -type f
./
./pattern
./File
./File/express.txt

$ find ./ -not -type f
./
./pattern
./File
./File/express.txt


NOT 연산은 위 2가지 형태로 사용합니다.
앞서 살펴본 AND, OR와는 다르게 단항연산자이며 표현식의 앞에 위치하며, 표현식을 만족하지 않는 대상만을 결과에 포함합니다.

1.4 (,)

  • 표현식1 , 표현식2 , 표현식3 ...
$ find ./ -name 'a*' , -name 'b*'
./bb.txt

$ find ./ \( -name 'a*' -printf '%p\n' \) , \( -name 'b*' -printf '%p\n' \)
./aa.txt
./bb.txt
./amin.txt


콤마 연산자는 여러개의 표현식을 실행할 때 사용됩니다.
실행 결과는 제일 마지막 표현식의 결과만 보여주는데, 표현식의 실행 결과를 모두 확인하기 위해서는 위 예시의 2번째 명령어 처럼 각각 -print액션을 사용해야 합니다.
-print는 뒤에서 액션을 소개할 때 자세히 다루도록 하겠습니다.

1.5 () 연산자

  • ( 표현식 )
$ find ./ -name 'a*' -print , -name 'b*' -print
./txtdir/aa.txt
./txtdir/bb.txt
./txtdir/amin.txt

$ find ./ \( -name 'a*' -print \) , \( -name 'b*' -print \)
./txtdir/aa.txt
./txtdir/bb.txt
./txtdir/amin.txt


소괄호 연산자는 표현식의 우선순위를 정의하거나 그룹핑을 해야하는 경우 사용합니다.
위 예시를 보면 소괄호 연산자를 사용했을 때 결과를 출력하는 순서가 다른 점을 알 수 있습니다.
주의사항으로는 반드시 괄호 앞에 역슬래시(\)를 사용해야한다는 점이 있습니다.

2. 액션

액션은 테스트와 같은 표현식의 결과를 인자로 받아 다른 처리를 해야할 때 사용합니다.
액션의 종류는 크게 명령어 실행 관련 액션, 결과 출력 관련 액션 2가지로 나눌 수 있습니다.
명령어 실행 관련 액션은 표현식의 결과를 인자로 받아서 명령어를 수행하고 싶을 때 사용합니다.
결과 출력 관련 액션은 표현식의 결과를 인자로 받아서 원하는 방식으로 출력하고 싶을 때 사용합니다.
액션의 종류는 다양하기 때문에 이번 시간에는 종류별로 실용적이라 생각되는 액션들만 다뤄보겠습니다.
그럼 먼저 명령어 실행 관련 액션부터 알아보겠습니다.

2.1 명령어 실행 관련 액션

명령어 실행 관련 액션의 대표적인 액션으로는 -exec가 있습니다.
사용하는 방법은 테스트의 뒤에 -exec 명령어{} \; 또는 -exec 명령어{} +;와 같이 사용하며, 중괄호({})에는 인자로 받은 표현식의 결과가 들어오게 됩니다.
앞서 소개한 사용 방법에서 명령어 뒤에 끝맺음을 할 때 세미콜론(;)플러스(+)를 사용하는것을 보셨을텐데요.
세미콜론(;)은 결과값을 줄바꿈해서 보여주고 역슬래시와 함께 사용해야하는 반면 플러스(+)는 결과값들을 이어서 보여준다는 차이점이 있습니다.
아래의 예시 코드에서 차이점을 확인할 수 있는데요, echo는 문자열을 받아서 터미널에 출력하는 명령어입니다.
;+의 실행 결과에 차이가 있는 것을 확인할 수 있습니다.

$ find ./ -name 'a*' -exec echo '{}' \;
./aa.txt
./amin.txt

$ find ./ -name 'a*' -exec echo '{}' +
./aa.txt ./amin.txt


조금 더 복잡한 예시를 살펴볼까요?
현재의 파일목록에서 확장자가 txt인 파일들을 txtdir이라는 디렉터리에 모으려고 합니다.
-exec를 활용하면 아래처럼 쉽게 처리할 수 있습니다!
참고로 파이프(|)를 사용하면 명령어를 이어서 실행할 수 있습니다.

$ mkdir txtdir | find ./ -name '*.txt' -exec mv {} txtdir \;
$ tree ./
./
├── File
│   └── File
├── expression.tar.gz
├── findtestfile
├── grep-test
├── pattern
│   └── findtestfile
└── txtdir
    ├── Separator.txt
    ├── aa.txt
    ├── amin.txt
    ├── bb.txt
    ├── express.txt -> ../expression.txt
    ├── expression.txt
    ├── file1.txt
    ├── file2.txt
    ├── pattern1.txt
    ├── pattern2.txt
    ├── pattern3.txt
    └── test.txt

3 directories, 17 files


2.2 결과 출력 관련 액션

결과 출력 관련 액션은 검색된 파일들을 원하는 포맷으로 출력하거나 검색 결과를 파일에 저장할 때 사용합니다.
액션으로는 -print, -ls, -fprint 파일, -fls 파일이 있습니다. -print-ls는 검색된 파일들을 터미널에 보여줍니다.
각 액션의 차이점은 -print는 파일들을 ls의 포맷으로 보여주고 -lsls -l의 출력 결과처럼 보여줍니다.
-fprint 파일-fls 파일은 각각 위 액션의 실행 결과를 파일로 저장합니다.
이 외에도 -printf, -fprintf등의 명령어가 있는데, 프로그래밍 언어의 printf처럼 원하는 포맷으로 출력할 수도 있습니다.

$ find ./ -name 'a*' -print
./txtdir/aa.txt
./txtdir/amin.txt

$ find ./ -name 'a*' -ls
1516989   68 -rw-rw----   1 centos   centos      65942  5월 15  2020 ./txtdir/aa.txt
1516991    4 -rw-------   1 centos   centos         30  5월 19  2020 ./txtdir/amin.txt

$ find ./ -name 'a*' -fprint f-file.txt
$ cat f-file.txt 
./txtdir/aa.txt
./txtdir/amin.txt

$ find ./ -name 'a*' -fls fls-file.txt
$ cat fls-file.txt 
1516989   68 -rw-rw----   1 centos   centos      65942  5월 15  2020 ./txtdir/aa.txt
1516991    4 -rw-------   1 centos   centos         30  5월 19  2020 ./txtdir/amin.txt

3. 위치옵션

일반적으로 위치옵션테스트의 수행 방법에 영향을 줍니다.
몇몇의 옵션을 제외하고는 테스트보다 먼저오나 나중에오나 항상 먼저 실행된다는 특징이 있습니다.
때문에 가독성을 위해서라도 옵션을 앞에 정의하는것이 좋습니다.
탐색 범위에서 CD-ROM, USB같은 외부 장치를 제외하는 -mount, 하위 디렉터리 깊이를 제한하는 -maxdepth 깊이등이 있습니다.
이 중에서는 확인이 수월한 -maxdepth 깊이에 대해서 살펴 보겠습니다.

3.1 maxdepth

$ find ./ -name 'p*'
./pattern
./txtdir/pattern1.txt
./txtdir/pattern2.txt
./txtdir/pattern3.txt

$ find ./ -maxdepth 1 -name 'p*'
./pattern
$ find ./ -maxdepth 2 -name 'p*'
./pattern
./txtdir/pattern1.txt
./txtdir/pattern2.txt
./txtdir/pattern3.txt


위 코드는 -maxdepth의 실행 결과를 비교한 내용입니다.
위치옵션을 사용하지 않으면 기본적으로 모든 하위 디렉터리를 대상으로 탐색합니다.
-maxdepth 1의 경우 현재 디렉터리에서만 탐색을 하는 반면 -maxdepth 2는 바로 하위 디렉터리까지 탐색을 하는 것을 확인할 수 있습니다.

마무리

이번에는 표현식에 속하는 연산자, 액션, 위치옵션에 대해서 알아보았습니다.
지금까지 알아본 표현식에 대한 이해만으로도 find를 활용하기에 충분하다고 생각합니다만, 짚고 넘어가지 않은 내용에 옵션이라는 기능이 있습니다.
옵션의 활용 방법에는 여러가지가 있는데 대표적으로는 find의 탐색 대상에서 심볼릭 링크파일은 어떻게 처리할 것인지에 대해 정의할 때 사용합니다.
이 부분에 대해서는 파일시스템의 링크에 대한 이해를 필요로합니다.
그래서 다음 글에서는 간단하게 링크란 무엇인지 먼저 알아보고 옵션에 대해서 가볍게 다뤄볼 예정입니다.

참고

  • 처음 배우는 셸 스크립트 - 한빛미디어

작성자: 성승익

작성일: 2021-09-21