`find`의 -exec 옵션 이해


53

나는 끊임없이 문법을 찾는다.

find . -name "FILENAME"  -exec rm {} \;

주로 -exec부품이 정확히 어떻게 작동 하는지 알 수 없기 때문 입니다. 중괄호, 백 슬래시 및 세미콜론의 의미는 무엇입니까? 해당 구문에 대한 다른 사용 사례가 있습니까?


11
@ 필리포스 : 당신의 요점이 보입니다. 매뉴얼 페이지는 참조, 즉 구문을 찾는 문제를 이해하는 사람들에게 유용하다는 것을 명심하십시오. 주제를 처음 접하는 사람에게는 종종 비판적이고 형식적으로 유용합니다. 허용 된 답변은 맨 페이지 항목 길이의 약 10 배이며, 그 이유가 있습니다.
Zsolt Szilagy

6
이전 POSIX man페이지 에서도 "{}"라는 두 문자 만 포함 된 utility_name 또는 인수는 현재 경로 이름 으로 바뀌어야하는데, 이는 나에게 충분합니다. 또한 -exec rm {} \;질문과 마찬가지로 예가 있습니다. 내 시대에는 "큰 회색 벽", 인쇄 된 man페이지의 책 (종이가 저장보다 더 칙칙함) 이외의 다른 리소스는 거의 없었습니다. 그래서 나는 이것이 주제를 처음 접하는 사람에게는 충분하다는 것을 알고 있습니다. 마지막 질문은 여기에서 요구하는 것이 공정합니다. 불행히도 @ Kusalananda 나 자신도 그것에 대한 답이 없습니다.
Philippos

1
Comeon @Philippos. Kusalananda에게 맨 페이지를 개선하지 않았다고 실제로 말하고 있습니까? :-)
Zsolt Szilagy

1
@allo xargs때로는 편리 하지만 find여러 경로 인수를 명령없이 전달할 수 있습니다. -exec command... {} +( +대신에 \;)를 사용하면 원하는만큼 많은 경로를 전달합니다 command...(각 OS에는 명령 줄 길이에 대한 자체 제한이 있습니다). 그리고 같은 xargs+의로 끝나는 형태 find의의 -exec행동은 실행 command...한계 내에서 맞게 너무 많은 경로가 드문 경우를 여러 번.
Eliah Kagan

2
@ ZsoltSzilagy 나는 그런 말도하지 않았다. 그는 당신에게 음식을 잘주었습니다 (-;
Philippos

답변:


90

이 답변은 다음과 같은 부분으로 구성됩니다.

  • 기본 사용법 -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.

  1. 검색을 더 제한하기위한 테스트로.
  2. 찾은 경로 이름에 대해 일종의 작업을 수행합니다 (일반적으로 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"

이것은 자식 쉘 스크립트에 두 개의 인수를 전달합니다.

  1. 문자열 sh입니다. $0스크립트 내부에서 사용할 수 있으며 내부 셸에서 오류 메시지가 출력되면이 문자열이 접두사로 사용됩니다.

  2. 인수 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" {} ';'

이 마지막 변형에서 변수 fromto자식 셸의 변수는 외부 스크립트에서 이름이 같은 변수와 다릅니다.

위는에서 임의의 복잡한 스크립트를 호출하는 올바른 방법 -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에서도 가능한 한 몇 번 실행됩니다. 이 마지막 예에는 mvcoreutils ( -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 {} +

7
-exec걸리는 프로그램과 인수를 하고 그것을 실행; 일부 쉘 명령은 프로그램과 인수로만 구성되어 있지만 대부분은 그렇지 않습니다. 쉘 명령에는 방향 재 지정 및 배관이 포함될 수 있습니다. -exec(전체 find를 리디렉션 할 수는 있지만) 할 수 없습니다 . 쉘 명령은 ; && ifetc 를 사용할 수 있습니다 . -exec비록 -a -o할 수는 있지만 할 수는 없습니다 . 쉘 명령은 별명 또는 쉘 함수 또는 내장 일 수 있습니다. -exec할 수 없습니다. 쉘 명령은 vars를 확장 할 수 있습니다. -exec캔을 실행하는 외부 쉘이지만 불가능 find합니다. 쉘 명령은 $(command)매번 다르게 대체 할 수 있습니다 . -exec할 수 없습니다. ...
dave_thompson_085

... 쉘 명령은 glob을 수행 -exec할 수 있습니다. find대부분의 globs와 동일한 방식으로 파일을 반복 할 수는 있기 때문에 거의 원하지 않습니다.
dave_thompson_085

@ dave_thompson_085 물론, 쉘 명령 sh자체 가 될 수 있습니다. 이 모든 기능을 완벽하게 수행 할 수 있습니다
Tavian Barnes

2
쉘 명령이 잘못되었다고 말하면 find -exec cmd arg \;쉘 명령 행을 해석하기 위해 쉘을 호출하지 않고 execlp("cmd", "arg")직접 실행 되지 않습니다 execlp("sh", "-c", "cmd arg")(쉘이 내장되지 않은 execlp("cmd", "arg")경우 와 동등한 기능을 수행합니다 cmd).
Stéphane Chazelas

2
당신은 명확 수있는 모든 find인수 후 -exec및 최대 ;또는 +의 각 인스턴스로, 인수와 함께 실행하는 명령을 구성하는 {}현재 파일 (와 대체 인수 ;), 그리고 {}마지막 인자로 이전 +파일의 목록으로 대체 별도의 인수로 ( {} +경우 에 따라 ). IOW이 -exec소요 여러 a로 종료 인수, ;또는 {} +.
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.