저는 계속해서
find . -name "FILENAME" -exec rm {} \;
주로 -exec
부분이 정확히 어떻게 작동하는지 알 수 없기 때문입니다. 중괄호, 백 슬래시 및 세미콜론의 의미는 무엇입니까? 다른 사용 사례가 있습니까? 그 구문?
댓글
답변
이 답변은 다음 부분으로 구성됩니다.
-
-exec
-
sh -c
와 함께-exec
사용 div> -
-exec ... {} +
사용 -
-execdir
사용
의 기본 사용법
-exec
의 기본 사용법
-exec
옵션은 다음과 같은 선택적 인수가있는 외부 유틸리티를 사용합니다. 인수 및 실행합니다.
{}
문자열이 주어진 명령의 아무 곳에 나 있으면 각 인스턴스는 현재 처리중인 경로 이름으로 대체됩니다 ( 예 : ./some/path/FILENAME
). 대부분의 쉘에서 두 문자 {}
는 따옴표로 묶을 필요가 없습니다.
명령어 find
가 끝나는 위치를 알기 위해 ;
로 종료되어야합니다 (나중에 추가 옵션이있을 수 있음). 에스). 셸에서 ;
를 보호하려면 \;
또는 ";"
로 인용해야합니다. , 그렇지 않으면 쉘에서 find
명령의 끝으로 인식합니다.
예 (\
처음 두 줄의 끝은 줄 연속만을위한 것입니다) :
find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} ";"
모든 일반 파일을 찾습니다 (-type f
)의 이름이 현재 디렉토리 안 또는 아래의 패턴 *.txt
와 일치합니다. 그런 다음 grep -q
를 사용하여 찾은 파일에서 hello
문자열이 발생하는지 테스트합니다 (출력을 생성하지 않고 종료 만 상태). 문자열이 포함 된 파일의 경우 cat
가 실행되어 파일의 내용을 터미널에 출력합니다.
각 -exec
는 -type
및 iv id = “3d1084b1c1과 마찬가지로 find
에서 찾은 경로 이름에 대한”테스트 “역할도합니다. “>
그렇습니다. 명령이 0 종료 상태 ( “성공”을 의미)를 반환하면find
명령의 다음 부분이 고려되고 그렇지 않으면find
명령은 다음 경로 이름으로 계속됩니다. 이것은 위의 예에서hello
문자열을 포함하는 파일을 찾는 데 사용되지만 다른 모든 파일은 무시합니다.
위의 예는 가장 일반적인 두 가지 사용을 보여줍니다. -exec
의 경우 :
- 검색을 추가로 제한하기위한 테스트
- 발견 된 항목에 대한 작업 수행 경로 이름 (일반적으로 반드시 그런 것은 아니지만
find
명령 끝)
와 sh -c
-exec
가 실행할 수있는 명령은 외부 유틸리티로 제한됩니다. 선택적 인수와 함께.-exec
와 함께 쉘 내장, 함수, 조건부, 파이프 라인, 리디렉션 등을 직접 사용하는 것은 sh -c
하위 셸.
bash
기능이 필요한 경우 iv id = 대신 bash -c
를 사용합니다. “73af3270ae”> .
sh -c
는 명령 줄에 제공된 스크립트로 /bin/sh
를 실행합니다. 그 뒤에 해당 스크립트에 대한 선택적 명령 줄 인수가옵니다.
find
없이 sh -c
를 단독으로 사용하는 간단한 예 :
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" {} ";"
Inte 내부 rnal 스크립트, $1
는 문자열 text
, $2
는 문자열 txt
및 $3
는 find
에서 찾은 경로 이름입니다. 매개 변수 확장 ${3%.$1}
는 경로 이름을 가져 와서 .text
접미사를 제거합니다.
또는
/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
및 는 외부 스크립트의 동일한 이름을 가진 변수와 다릅니다.
위는 (find
포함). find
를
for pathname in $( find ... ); do
와 같은 루프에서 사용하면 오류가 발생하기 쉽고 우아하지 않습니다 (개인적 의견). 공백에서 파일 이름을 분할하고 파일 이름 globbing을 호출하며 루프의 첫 번째 반복을 실행하기 전에 쉘이 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
는 다음과 같이 실행됩니다. 가능한 한 몇 번. 이 마지막 예에는 coreutils의 GNU mv
가 필요합니다 (-t
옵션 지원).
-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
를 사용하는 해당 작업 div>는 하위 셸을 사용해야합니다.
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 {} +
주석
-
-exec
는 프로그램 및 인수 를 가져와 실행합니다. 일부 쉘 명령은 프로그램과 인수로만 구성되지만 대부분은 그렇지 않습니다. 쉘 명령에는 리디렉션 및 파이프가 포함될 수 있습니다.-exec
는 할 수 없습니다 (전체find
는 리디렉션 될 수 있음). 쉘 명령은; && if
등을 사용할 수 있습니다.-exec
는 할 수 없지만-a -o
는 할 수 있습니다. 쉘 명령은 별명, 쉘 함수 또는 내장 일 수 있습니다.-exec
는 할 수 없습니다. 쉘 명령은 vars를 확장 할 수 있습니다.-exec
는 할 수 없습니다 (find
를 실행하는 외부 셸은 할 수 있지만). 셸 명령은 매번$(command)
를 다르게 대체 할 수 있습니다.-exec
는 할 수 없습니다. … - ‘ 여기서 쉘 명령이 잘못되었습니다.
find -exec cmd arg \;
는 ‘ 셸 명령 줄을 해석하기 위해 셸을 호출하지 않고execlp("sh", "-c", "cmd arg")
가 아닌execlp("cmd", "arg")
를 직접 실행합니다. 쉘은cmd
가 내장되어 있지 않은 경우execlp("cmd", "arg")
와 동일한 작업을 수행합니다. - 모든 것을 명확히 할 수 있습니다.
-exec
뒤의find
인수 및 최대;
또는+
인수와 함께 실행할 명령을 구성하고{}
인수의 각 인스턴스를 현재 파일 (;
),+
이전의 마지막 인수로{}
가 별도의 인수로 파일 목록으로 대체되었습니다 ({} +
사례). IOW-exec
는;
또는{}
으로 끝나는 여러 인수를받습니다. div>+
. - @Kusalananda는 ‘ 마지막 예제도 다음과 같은 간단한 명령으로 작동하지 않을 것입니다.
find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'
? - @Atralb 예, 작동했을 수도 있고 마지막 코드와 동일한 효과를 보였지만 루프에서 발견 된 파일 당 한 번씩 발견 된 각 파일에 대해
sh
및mv
를 모두 실행합니다. 대용량 파일의 경우 눈에 띄게 느립니다.
man
페이지에서도 유틸리티 _ 이름 또는 인수를 읽습니다. 두 문자 만 포함하는 ” {} “는 대체됩니다. d는 현재 경로 이름 으로 충분합니다. 추가로 귀하의 질문과 마찬가지로-exec rm {} \;
에 대한 예가 있습니다. 제 시대에는 ” 큰 회색 벽 “, 인쇄 된 페이지 (종이는 저장 공간보다 저렴했습니다). 그래서 나는 이것이 주제를 처음 접하는 사람에게 충분하다는 것을 알고 있습니다. 마지막 질문은 여기서 물어볼 수 있습니다. 안타깝게도 @Kusalananda도 나 자신도 이에 대한 답을 갖고 있지 않습니다.