나는 끊임없이 구문을 찾고 있음을 발견합니다.
find . -name "FILENAME" -exec rm {} \;
-exec
주로 그 부분이 정확히 어떻게 작동하는지 이해하지 못하기 때문입니다 . 중괄호, 백슬래시 및 세미콜론은 무엇을 의미합니까? 이 구문에 대한 다른 사용 사례가 있습니까?
답변1
이 답변은 다음 부분으로 구분됩니다.
- 기본 사용법
-exec
-exec
와 함께sh -c
- 사용
-exec ... {} +
- 사용
-execdir
기본 사용법-exec
이 -exec
옵션은 선택적 매개변수가 있는 외부 유틸리티를 인수로 사용하여 실행합니다.
이 문자열이 {}
지정된 명령의 어느 위치에나 나타나면 이 문자열의 각 인스턴스는 현재 처리 중인 경로 이름(예: ./some/path/FILENAME
)으로 대체됩니다. 대부분의 쉘에서는 이 두 문자를 {}
따옴표로 묶을 필요가 없습니다.
명령 이 끝나는 위치를 알기 위해서는 명령이 for로 ;
끝나야 합니다(그 뒤에 더 많은 옵션이 있을 수 있기 때문입니다). 셸로부터 find
보호하려면 or 로 인용해야 합니다 . 그렇지 않으면 셸에서 이를 명령의 끝으로 처리합니다 .;
\;
';'
find
예( \
처음 두 줄의 끝에 연속 전용 줄):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
그러면 현재 디렉토리 안이나 아래의 -type f
패턴과 이름이 일치하는 모든 일반 파일()을 찾습니다. 그런 다음 해당 문자열이 발견된 파일에 나타나는지 *.txt
테스트합니다 (출력은 없고 종료 상태만 생성). 이 문자열이 포함된 파일의 경우 파일 내용이 터미널에 출력됩니다.hello
grep -q
cat
각각은 및 와 마찬가지로 -exec
발견된 경로 이름에 대한 "테스트"와 유사합니다 . 명령이 종료 상태 0("성공"을 나타냄)을 반환하면 명령의 다음 부분이 고려되고, 그렇지 않으면 명령이 다음 경로 이름으로 계속됩니다. 이는 위의 예에서 string 을 포함하는 파일을 찾는 데 사용되지만 다른 모든 파일은 무시됩니다.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"
이는 서브쉘 스크립트에 두 개의 매개변수를 전달합니다. 이는 스크립트에 배치되어 사용 $0
됩니다 .$1
끈
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
string text
, $2
string 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
그렇지 sfind
않습니다. ).
예:
find . -type f -name '*.txt' \
-execdir mv -- {} 'done-texts/{}.done' \;
이렇게 하면 발견된 각 *.txt
파일이 기존 done-texts
하위 디렉터리 로 이동됩니다.파일이 발견된 디렉토리와 동일한 디렉토리. .done
접미사를 추가하여 파일 이름도 변경됩니다 . , 기본 이름 앞에 . 을 붙이지 않는 구현에서는 --
옵션의 끝을 표시해야 합니다. 쉘이 포함된 경우 전체가 포함되지 않은 인수를 묶어야 합니다. 또한 모든 구현이 그곳으로 확장되는 것은 아닙니다(그렇지는 않습니다).find
./
{}
(t)csh
find
{}
sfind
파일의 새 이름을 구성 -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 {} +