-exec COMMAND \;
find가 찾아낸 각각의 파일에 대해 COMMAND를 실행합니다. COMMAND는 \;으로 끝나야 합니다(find로 넘어가는 명령어의 끝을 나타내는 ;를 쉘이 해석하지 않도록 이스케이프 시켜야 합니다). COMMAND에 {}이 포함되어 있으면 선택된 파일을 완전한 경로명으로 바꿔 줍니다.
bash$ find ~/ -name '*.txt' /home/bozo/.kde/share/apps/karm/karmdata.txt /home/bozo/misc/irmeyc.txt /home/bozo/test-scripts/1.txt |
find /home/bozo/projects -mtime 1 # /home/bozo/projects 디렉토리안에 있는 파일중에서 # 하루 전에 변경된 파일들을 모두 보여 줍니다. |
find /etc -exec grep '[0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*[.][0-9][0-9]*' {} \; # /etc 디렉토리에 들어 있는 파일들에 포함된 # 모든 IP 주소(xxx.xxx.xxx.xxx)를 찾아줍니다. # IP 가 아닌 것도 나오는데 이것들을 어떻게 걸러낼 수 있을까요? # 이건 어때요? find /etc -type f -exec cat '{}' \; | tr -c '.[:digit:]' '\n' \ | grep '^[^.][^.]*\.[^.][^.]*\.[^.][^.]*\.[^.][^.]*$' # Thanks, S.C. |
경고 |
find에서 쓰이는 -exec 옵션을 쉘 내장 명령인 exec과 헷갈리면 안 됩니다. |
예 12-2. Badname, 파일 이름에 일반적이지 않은 문자나 공백 문자를 포함하는 파일을 지우기.
#!/bin/bash # 현재 디렉토리에 들어 있는 파일중, 이름에 정상적이지 않은 글자가 포함된 파일을 지우기 for filename in * do badname=`echo "$filename" | sed -n /[\+\{\;\"\\\=\?~\(\)\<\>\&\*\|\$]/p` # 이런 고약한 글자를 포함하는 파일들: + { ; " \ = ? ~ ( ) < > & * | $ rm $badname 2>/dev/null # 에러 메세지는 무시. done # 다음은 모든 종류의 공백 문자를 포함하는 파일들을 처리하겠습니다. find . -name "* *" -exec rm -f {} \; # "find"가 찾은 파일이름이 "{}"로 바뀝니다. # '\'를 써서 ';'가 명령어 끝을 나타낸다는 원래의 의미로 해석되게 합니다. exit 0 #--------------------------------------------------------------------- # 다음의 명령어들은 위에서 "exit"를 했기 때문에 실행되지 않습니다. # 위 스크립트의 다른 방법: find . -name '*[+{;"\\=?~()<>&*|$ ]*' -exec rm -f '{}' \; exit 0 # (Thanks, S.C.) |
예 12-3. inode 로 파일을 지우기
#!/bin/bash # idelete.sh: inode 로 파일을 지우기. # 이 스크립트는 파일이름이 ? 나 - 처럼 부적절한 문자로 시작될 때 유용합니다. ARGCOUNT=1 # 인자로 파일이름이 필요함. E_WRONGARGS=70 E_FILE_NOT_EXIST=71 E_CHANGED_MIND=72 if [ $# -ne "$ARGCOUNT" ] then echo "사용법: `basename $0` filename" exit $E_WRONGARGS fi if [ ! -e "$1" ] then echo "\""$1"\" 는 존재하지 않는 파일입니다." exit $E_FILE_NOT_EXIST fi inum=`ls -i | grep "$1" | awk '{print $1}'` # inum = 파일의 inode (index node) # 모든 파일은 자신의 물리적 주소 정보를 담고 있는 inode를 갖고 있습니다. echo; echo -n "\"$1\" 를 진짜로 지우실 겁니까(y/n)? " read answer case "$answer" in [nN]) echo "마음을 바꿨군요, 그렇죠?" exit $E_CHANGED_MIND ;; *) echo "\"$1\" 를 지우는 중.";; esac find . -inum $inum -exec rm {} \; echo ""\"$1"\" 가 지워졌습니다!" exit 0 |
스크립트에서 find를 사용하는 다음 예제들을 참고하세요. 예 12-22, 예 4-3, 예 10-8. 이 복잡하고 강력한 명령어에 대해서 더 알고 싶으면 맨페이지를 살펴보세요.
명령어에 인자들을 필터링해서 넘겨 주고 그 명령어를 다시 조합하는 데 쓸 수도 있습니다. xargs는 입력을 필터용으로 작게 조각내서 명령어가 처리하게 해 줍니다. 역따옴표의 강력한 대용품이라고 생각하면 됩니다. 역따옴표를 써서 too many arguments란 에러가 났을 때, xargs로 바꿔 쓰면 성공할 수도 있습니다. 보통은 표준 입력이나 파이프에서 데이터를 읽어 들이지만 파일의 출력에서도 읽을 수 있습니다.
xargs의 기본 명령어는 echo입니다.
ls | xargs -p -l gzip 은 현재 디렉토리의 모든 파일에 대해 한번에 한 파일씩 물어보면서 gzips으로 묶어줍니다.
작은 정보: 재밌는 옵션중 하나인 -n XX을 쓰면 넘길 인자의 갯수를 XX로 제한합니다.
ls | xargs -n 8 echo 는 현재 디렉토리의 파일들을 한 줄에 8 개씩 끊어서 보여줍니다.
작은 정보: 다른 유용한 옵션으로 find -print0나 grep -lZ와 함께 쓰는 -0이 있습니다. 이 옵션은 공백 문자나 따옴표가 들어간 인자를 처리할 수 있게 해줍니다.
find / -type f -print0 | xargs -0 grep -liwZ GUI | xargs -0 rm -f
grep -rliwZ GUI / | xargs -0 rm -f
위의 두 가지 모두 "GUI"를 포함하고 있는 어떤 파일도 지워 줍니다. (Thanks, S.C.)
예 12-4. 시스템 로그 모니터링용 xargs 로그 파일
#!/bin/bash # 현재 디렉토리에 /var/log/messages 의 끝 부분을 포함하는 로그 파일을 만들기 # 주의: 일반 사용자도 이 스크립트를 쓰게 하려면 # 루트로 chmod 644 /var/log/messages 라고 해서 # 누구나 /var/log/messages 를 읽을 수 있게 해야 됩니다. LINES=5 ( date; uname -a ) >>logfile # 시간과 머신 이름 echo --------------------------------------------------------------------- >>logfile tail -$LINES /var/log/messages | xargs | fmt -s >>logfile echo >>logfile echo >>logfile exit 0 |
다목적 표현식 평가 명령어: 주어진 연산에 따라 자동으로 계산하거나 평가합니다. 이 때, 인자는 빈칸으로 분리되어야 합니다. 산술, 비교, 문자열, 논리 연산등이 가능합니다.
8 리턴
2 리턴
변수를 증가. let y=y+1 이나 y=$(($y+1)) 과 같음. 이것은 산술 확장 예제입니다.
$string의 $position에서부터 $length만큼의 문자열조각(substring)을 추출해 냄.
예 12-6. expr 쓰기
#!/bin/bash # 'expr'의 몇가지 사용법 보여주기 # =============================== echo # 산술 연산자 # ---- ------ echo "산술 연산자" echo a=`expr 5 + 3` echo "5 + 3 = $a" a=`expr $a + 1` echo echo "a + 1 = $a" echo "(변수 증가)" a=`expr 5 % 3` # 나머지(modulo) echo echo "5 mod 3 = $a" echo echo # 논리 연산자 # ---- ------ # 참이면 1, 거짓이면 0 리턴. # Bash 관례와 반대입니다. echo "논리 연산자" echo x=24 y=25 b=`expr $x = $y` # 같은 값인지 확인하기. echo "b = $b" # 0 ( $x -ne $y ) echo a=3 b=`expr $a \> 10` echo 'b=`expr $a \> 10`, 즉...' echo "If a > 10, b = 0 (거짓)" echo "b = $b" # 0 ( 3 ! -gt 10 ) echo b=`expr $a \< 10` echo "If a < 10, b = 1 (참)" echo "b = $b" # 1 ( 3 -lt 10 ) echo # 연산자를 이스케이프 시킨것에 주의. b=`expr $a \<= 3` echo "If a <= 3, b = 1 (참)" echo "b = $b" # 1 ( 3 -le 3 ) # "\>=" 연산자도 있어요(크거나 같음). echo echo # 비교 연산자 # ---- ------ echo "비교 연산자" echo a=zipper echo "a 는 $a" if [ `expr $a = snap` ] # 변수 'a'를 강제로 재평가(re-evaluation) then echo "a 는 zipper 가 아님" fi echo echo # 문자열 연산자 # ------ ------ echo "문자열 연산자" echo a=1234zipper43231 echo "\"$a\" 를 가지고 조작해 보겠습니다." # length: 문자열 길이 b=`expr length $a` echo "\"$a\" 의 길이는 $b." # index: 문자열에서 문자열조각(substring)이 일치하는 첫번째 문자의 위치 b=`expr index $a 23` echo "\"$a\" 에서 \"2\" 가 첫번째로 나오는 위치는 \"$b\" 입니다." # substr: 문자열조각 추출, 추출할 시작 위치와 추출할 길이 지정 b=`expr substr $a 2 6` echo "시작위치는 2이고 길이가 6인 \"$a\" 의 문자열조각은 \"$b\" 입니다." # 'match' 연산은 정규표현식을 쓰는 'grep'과 비슷합니다. b=`expr match "$a" '[0-9]*'` echo \"$a\" 에서 앞쪽에 나오는 숫자의 갯수는 $b 입니다. b=`expr match "$a" '\([0-9]*\)'` # 중괄호가 이스케이프된 것에 주의하세요. echo "\"$a\" 에서 앞쪽에 나오는 숫자는 \"$b\" 입니다." echo exit 0 |
중요: match 대신 : 연산자를 쓸 수 있습니다. 예를 들면, 위의 예제에서 b=`expr $a : [0-9]*` 은 b=`expr match $a [0-9]*` 과 완전히 동일합니다.
#!/bin/bash echo echo "\"expr $string :\" 를 쓰는 문자열 연산" echo "--------------------------------------" echo a=1234zipper43231 echo "\"`expr "$a" : '\(.*\)'`\" 를 가지고 문자열 연산을 합니다." # 이스케이프된 소괄호. # 정규표현식 파싱. echo "\"$a\" 의 길이는 `expr "$a" : '.*'` 입니다." # 문자열 길이 echo "\"$a\" 에서 앞쪽에 나오는 숫자의 갯수는 `expr "$a" : '[0-9]*'` 입니다." echo "\"$a\" 에서 앞쪽에 나오는 숫자는 `expr "$a" : '\([0-9]*\)'` 입니다." echo exit 0
펄과 sed가 더 뛰어난 문자열 파싱 능력을 가지고 있기 때문에 스크립트에서 Perl이나 sed의 간단한 "서브루틴"을 쓰는 것이 expr을 쓰는 것 보다 더 좋은 방법입니다.
문자열 연산에 대한 더 자세한 사항은 9.2절을 참고하세요.