이 답변은 다음과 같은 부분으로 구성됩니다.
- 기본 사용법
-exec
- 사용
-exec
조합으로sh -c
- 사용
-exec ... {} +
- 사용
-execdir
기본 사용법 -exec
이 -exec
옵션은 선택적 인수가있는 외부 유틸리티를 인수로 사용하여 실행합니다.
문자열 {}
이 주어진 명령의 어느 곳에 나 존재하면, 각 인스턴스는 현재 처리되고있는 경로 이름 (예 :)으로 대체됩니다 ./some/path/FILENAME
. 대부분의 쉘에서 두 문자 {}
는 인용 할 필요가 없습니다.
명령은 종료 위치를 알 수 있도록 ;
for find
로 종료해야합니다 (나중에 추가 옵션이있을 수 있음). ;
쉘 을 보호하려면 쉘을 \;
또는 로 인용해야합니다 ';'
. 그렇지 않으면 쉘이이를 find
명령 의 끝으로 간주합니다 .
예 ( \
처음 두 줄의 끝에는 줄 연속을위한 것임) :
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
현재 디렉토리 내 또는 아래의 -type f
패턴과 이름이 일치하는 모든 일반 파일 ( ) 을 찾습니다 *.txt
. 그런 다음 (출력을 생성하지 않고 종료 상태 만 생성 함)을 hello
사용하여 찾은 파일에서 문자열이 발생 하는지 테스트합니다 grep -q
. 문자열이 포함 cat
된 파일의 경우 파일 내용을 터미널로 출력하기 위해 실행됩니다.
각 -exec
도에 의해 발견 된 경로 이름에 "테스트"와 같은 역할 find
처럼, -type
그리고 -name
않습니다. 명령이 종료 성공 상태 ( "성공"을 나타냄)를 리턴하면 명령의 다음 부분 find
이 고려되고, 그렇지 않으면 find
명령은 다음 경로 이름으로 계속됩니다. 위의 예제에서 문자열이 포함 된 파일을 찾기 위해 사용 hello
되지만 다른 모든 파일은 무시합니다.
위의 예는 가장 일반적인 두 가지 사용 사례를 보여줍니다 -exec
.
- 검색을 더 제한하기위한 테스트로.
- 찾은 경로 이름에 대해 일종의 작업을 수행합니다 (일반적으로
find
명령 의 끝에서 반드시 그런 것은 아님 ).
사용 -exec
조합으로sh -c
-exec
실행할 수 있는 명령은 선택적 인수가있는 외부 유틸리티로 제한됩니다. 쉘 내장, 함수, 조건부, 파이프 라인, 리디렉션 등을 직접 사용 -exec
하는 것은 sh -c
자식 쉘 과 같은 것으로 싸여 있지 않으면 불가능합니다 .
bash
기능이 필요한 경우 bash -c
대신에 사용하십시오 sh -c
.
sh -c
/bin/sh
명령 행에 제공된 스크립트로 실행 한 다음 해당 스크립트에 대한 선택적 명령 행 인수를 사용합니다.
다음을 사용 sh -c
하지 않고 자체적 으로 사용하는 간단한 예 find
:
sh -c 'echo "You gave me $1, thanks!"' sh "apples"
이것은 자식 쉘 스크립트에 두 개의 인수를 전달합니다.
문자열 sh
입니다. $0
스크립트 내부에서 사용할 수 있으며 내부 셸에서 오류 메시지가 출력되면이 문자열이 접두사로 사용됩니다.
인수 apples
로 사용할 수 있습니다 $1
스크립트, 거기 이상의 인수 한 후 이들로 사용할 수 있었을 것이다 있었다 $2
, $3
등 그들은 또한 목록에서 사용할 수있는 것 "$@"
(을 제외 $0
의 일부가 될 것이다 "$@"
).
이것은에 -exec
의해 발견 된 경로명에 작용하는 임의의 복잡한 스크립트를 만들 수있게하므로 유용합니다 find
.
예 : 특정 파일 이름 접미사가있는 모든 일반 파일을 찾고 해당 파일 이름 접미사를 다른 접미사로 변경하십시오. 접미사가 변수로 유지됩니다.
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'
내부 스크립트 내부 $1
문자열 것 text
, $2
문자열 것 txt
하고 $3
어떤 경로 것 find
우리를 발견했다. 매개 변수 확장 ${3%.$1}
은 경로 이름을 가져 와서 접미 부를 제거합니다 .text
.
또는 사용하여 dirname
/ basename
:
find . -type f -name "*.$from" -exec sh -c '
mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'
또는 내부 스크립트에 변수가 추가 된 경우 :
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2; pathname=$3
mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'
이 마지막 변형에서 변수 from
와 to
자식 셸의 변수는 외부 스크립트에서 이름이 같은 변수와 다릅니다.
위는에서 임의의 복잡한 스크립트를 호출하는 올바른 방법 -exec
으로 find
. find
같은 루프에서 사용
for pathname in $( find ... ); do
오류가 발생하기 쉽고 우아하지 않습니다 (개인 의견). 파일 이름을 공백으로 나누고 파일 이름 글 로빙을 호출 find
하며 루프의 첫 번째 반복을 실행하기 전에 쉘이 완전한 결과를 확장하도록 강제합니다 .
또한보십시오:
사용 -exec ... {} +
;
끝은 대체 될 수있다 +
. 이로 인해 find
찾은 각 경로 이름에 대해 가능한 한 많은 인수 (발견 된 경로 이름)로 지정된 명령이 실행됩니다. 이 작업을 수행 {}
하기 직전에 문자열 이 발생 +
해야합니다 .
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
여기서 find
결과 경로 이름을 수집하고 cat
가능한 한 많은 경로 이름 을 한 번에 실행합니다.
find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
마찬가지로 여기 mv
에서도 가능한 한 몇 번 실행됩니다. 이 마지막 예에는 mv
coreutils ( -t
옵션 을 지원하는 )의 GNU 가 필요합니다 .
사용 -exec sh -c ... {} +
은 임의로 복잡한 스크립트로 경로 이름 세트를 반복하는 효율적인 방법입니다.
기본은를 사용할 때와 동일 -exec sh -c ... {} ';'
하지만 스크립트는 훨씬 더 긴 인수 목록을 사용합니다. "$@"
스크립트 내에서 반복하여 반복 할 수 있습니다 .
파일 이름 접미사를 변경하는 마지막 섹션의 예제 :
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2
shift 2 # remove the first two arguments from the list
# because in this case these are *not* pathnames
# given to us by find
for pathname do # or: for pathname in "$@"; do
mv "$pathname" "${pathname%.$from}.$to"
done' sh "$from" "$to" {} +
사용 -execdir
또한 -execdir
(대부분의 find
변형으로 구현 되지만 표준 옵션은 아닙니다).
이 같은 작품 -exec
주어진 쉘 명령은 현재 작업 디렉토리로 발견 된 경로 이름의 디렉토리에 실행되고 그 차이와 {}
그 경로없이 발견 된 경로의 기본 이름이 포함됩니다 (그러나 GNU는 find
여전히와베이스 이름을 앞에 것 ./
, BSD 동안 find
그렇지 않습니다).
예:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
이렇게하면 각 찾은 파일이 파일이있는 디렉토리와 같은 디렉토리에있는*.txt
기존의 done-texts
서브 디렉토리로 이동합니다 . 파일에 접미사 .done
를 추가하여 파일 이름도 바꿉니다 .
-exec
파일 {}
의 새로운 이름을 형성하기 위해 발견 된 파일의 기본 이름을 가져와야하기 때문에 이것은 조금 까다로울 것 입니다. 또한 디렉토리를 올바르게 {}
찾으려면 done-texts
디렉토리 이름이 필요합니다 .
를 사용하면 이와 -execdir
같은 것들이 더 쉬워집니다.
-exec
대신에 사용하는 해당 작업 -execdir
에는 자식 셸이 사용되어야합니다.
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
done' sh {} +
또는,
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "${name%/*}/done-texts/${name##*/}.done"
done' sh {} +