리눅스에서 -exec 쉘 기능을 찾으십니까?


185

find셸에서 정의한 함수를 실행 하는 방법이 있습니까? 예를 들면 다음과 같습니다.

dosomething () {
  echo "doing something with $1"
}
find . -exec dosomething {} \;

그 결과는 다음과 같습니다.

find: dosomething: No such file or directory

얻을 수있는 방법이 있습니까 find의를 -exec볼 수는 dosomething?

답변:


262

쉘만이 쉘 함수를 실행하는 방법을 알고 있으므로 함수를 실행하려면 쉘을 실행해야합니다. 또한로 내보내기를 위해 함수를 표시해야합니다 export -f. 그렇지 않으면 서브 쉘이이를 상속하지 않습니다.

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;

7
네가 날 이겼어. 그런데 괄호를 사용하는 대신 따옴표 안에 넣을 수 있습니다 $0.
추후 공지가있을 때까지 일시 중지되었습니다.

20
당신이 find . -exec bash -c 'dosomething "$0"' {} \;그것을 사용 하면 파일 이름에서 공백 (및 다른 이상한 문자)을 처리합니다 ...
Gordon Davisson

3
@alxndr : 그 등 큰 따옴표, 역 인용 부호, 달러 표시, 일부 탈출 콤보, ...와 파일 이름에 실패 할 것이다
고든 데이비슨

4
또한 -f를 내 보내지 않으면 함수 가 호출하는 함수를 사용할 수 없습니다.
hraban

3
export -fbash는 일부 버전에서만 작동합니다. 크로스 플랫폼이 아닌 posix가 아니며 /bin/sh오류가 발생합니다.
Роман Коптев

120
find . | while read file; do dosomething "$file"; done

10
좋은 해결책. 함수를 내보내거나 이스케이프 인수를 어지럽 힐 필요가 없으며 각 함수를 실행하기 위해 서브 쉘을 생성하지 않기 때문에 아마도 더 효율적입니다.
Tom

19
그러나 개행 문자가 포함 된 파일 이름은 손상 될 수 있습니다.
chepner

3
매번 완전히 새로운 쉘 / 환경을 만들지 않고 전역 변수와 함수를 사용할 수 있기 때문에 이것은 "쉘 리쉬"입니다. Adam의 방법을 시도하고 모든 종류의 환경 문제를 겪은 후 어려운 방법을 배웠습니다. 이 방법은 또한 모든 내보내기로 현재 사용자의 쉘을 손상시키지 않으며 더 적은 훈련이 필요합니다.
Ray Foss

서브 쉘없이 허용되는 답변보다 훨씬 빠릅니다! 좋은! 감사합니다!
Bill Heller

2
또한 while readfor 루프를 변경 하여 문제를 해결했습니다 . for item in $(find . ); do some_function "${item}"; done
user5359531

22

위의 Jak의 대답은 훌륭하지만 쉽게 극복 할 수있는 몇 가지 함정이 있습니다.

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

줄 바꿈 대신 구분 기호로 null을 사용하므로 줄 바꿈이있는 파일 이름이 작동합니다. 또한 -r파일 이름의 백 슬래시가 작동하지 않으면 백 슬래시 이스케이프를 비활성화 하는 플래그를 사용합니다. 또한 IFS이름의 잠재적 인 후행 공백이 삭제되지 않도록 지 웁니다 .


1
/bin/bash좋지만 작동하지 않습니다 /bin/sh. 안타까워 라.
Роман Коптев

@ РоманКоптев 적어도 / bin / bash에서 작동하는 것이 얼마나 운이 좋은지.
sdenham

21

{}아래와 같이 따옴표를 추가하십시오 .

export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;

find예를 들어 이름에 괄호가있는 파일과 같은 특수 문자로 인해 발생하는 모든 오류를 수정합니다 .


1
올바른 사용법이 아닙니다 {}. 큰 따옴표가 포함 된 파일 이름이 손상됩니다. touch '"; rm -rf .; echo "I deleted all you files, haha. 죄송합니다.
gniourf_gniourf

예, 이것은 매우 나쁩니다. 주사로 이용할 수 있습니다. 매우 안전하지 않습니다. 이것을 사용하지 마십시오!
도미니크

1
@kdubs : 명령 문자열 내에서 $ 0 (인용되지 않은)을 사용하고 파일 이름을 첫 번째 인수로 전달하십시오. -exec bash -c 'echo $0' '{}' \;을 사용할 때 bash -c$ 0은 스크립트 이름이 아닌 첫 번째 인수입니다.
sdenham

10

대량 처리 결과

효율성을 높이기 위해 많은 사람들이 xargs결과를 대량으로 처리 하는 데 사용 하지만 매우 위험합니다. 그 때문에 find대량으로 결과를 실행 하는 대체 방법이 도입되었습니다 .

이 방법은 POSIX-의 요구 사항은, 예를 들어 같은 몇 가지주의와 함께 수도 있지만주의 find해야하는 {}명령의 끝에서.

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

find는 많은 결과를 단일 호출에 대한 인수로 전달 bash하고 for-loop는 해당 인수를 반복 dosomething하여 각 인수 에 대해 함수 를 실행합니다 .

위의 솔루션은에서 인수를 시작 $1하므로 _(가 나타내는 $0)가 있습니다.

하나씩 결과 처리

같은 방법으로, 나는 받아 들여진 최고 답변이 다음과 같이 수정되어야한다고 생각합니다

export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;

인수가 항상 시작해야하기 때문에,뿐만 아니라 더 제정신 $1뿐만 아니라 사용하여 $0파일 이름에 의해 반환 된 경우 예기치 않은 동작이 발생할 수 있습니다 find쉘에 특별한 의미가 있습니다.


9

스크립트를 호출하여 각 항목을 인수로 전달하십시오.

#!/bin/bash

if [ ! $1 == "" ] ; then
   echo "doing something with $1"
   exit 0
fi

find . -exec $0 {} \;

exit 0

스크립트를 단독으로 실행하면 찾고자하는 것을 찾고 각 찾기 결과를 인수로 전달하여 자체를 호출합니다. 스크립트를 인수와 함께 실행하면 인수에서 명령을 실행 한 다음 종료됩니다.


멋진 아이디어이지만 나쁜 스타일 : 두 가지 목적으로 동일한 스크립트를 사용합니다. bin /의 파일 수를 줄이려면 모든 스크립트를 시작시 큰 사례 절이있는 단일 스크립트로 병합 할 수 있습니다. 매우 깨끗한 해결책입니까?
user829755

말할 것도없이 ...로 find: ‘myscript.sh’: No such file or directory시작 하면 실패합니다 bash myscript.sh...
Camusensei

5

현재 디렉토리의 모든 파일에서 주어진 명령을 실행할 bash 함수를 찾고있는 사람들을 위해 위의 답변 중 하나를 컴파일했습니다.

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

공백이 포함 된 파일 이름으로 구분됩니다 (아래 참조).

예를 들어 다음 기능을 사용하십시오.

world(){
    sed -i 's_hello_world_g' "$1"
}

현재 디렉토리의 모든 파일에서 hello의 모든 인스턴스를 world로 변경하고 싶다고 가정 해보십시오. 난 그럴거야:

toall world

파일 이름의 기호를 안전하게 사용하려면 다음을 사용하십시오.

toall(){
    find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}

(하지만 GNU와 같은 find것을 처리 해야합니다 ).-print0find


3

하나의 작업으로 두 명령을 반복하여 다음과 같이 가장 쉬운 방법을 찾습니다.

func_one () {
  echo "first thing with $1"
}

func_two () {
  echo "second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done

3

참고로 다음을 사용 하여이 시나리오를 피하십시오.

for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
  _script_function_call $i;
done;

현재 스크립트 파일에서 찾기 출력을 가져오고 원하는대로 출력을 반복하십시오. 나는 대답에 동의하지만 스크립트 파일 외부에서 기능을 노출하고 싶지 않습니다.


이것은 크기 제한이 있습니다
Richard

2

그런 식으로 함수 를 실행할 수는 없습니다 .

이것을 극복하기 위해 쉘 스크립트에 함수를 넣고 다음에서 호출 할 수 있습니다 find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

이제 다음과 같이 찾으십시오.

find . -exec dosomething.sh {} \;

~ / bin에서 더 많은 파일을 피하려고했습니다. 그래도 고마워!
alxndr

나는 downvoting을 고려했지만 솔루션 자체는 나쁘지 않습니다. 올바른 인용 dosomething $1dosomething "$1"find . -exec bash dosomething.sh {} \;
부호를

2

함수를 별도의 파일에 넣고 find실행하십시오.

셸 함수는 정의 된 셸 내부에 있습니다. find그들을 볼 수 없습니다.


잡았다; 말이된다. 그래도 ~ / bin에서 더 많은 파일을 피하려고했습니다.
alxndr

2

다른 답변에 대한 추가 및 설명을 제공하려면 exec또는 execdir( -exec command {} +)에 대량 옵션을 사용하고 모든 위치 인수를 검색하려는 경우 $0with 처리를 고려해야합니다 bash -c. 보다 구체적으로, bash -c위에서 제안한대로 사용하는 아래 명령을 고려하고 찾은 각 디렉토리에서 '.wav'로 끝나는 파일 경로를 에코합니다.

find "$1" -name '*.wav' -execdir bash -c 'echo $@' _ {} +

bash 매뉴얼은 다음과 같이 말합니다.

If the -c option is present, then commands are read from the first non-option argument command_string.  If there are arguments after the command_string, they  are  assigned  to  the
                 positional parameters, starting with $0.

여기, 'check $@'명령 문자열이 있으며 명령 문자열 _ {}뒤의 인수입니다. 참고 $@모든 위치 매개 변수로 확장가 떠들썩한 파티에서 특별한 위치 매개 변수 1부터 시작이 . 또한 -c옵션을 사용하면 첫 번째 인수가 위치 매개 변수에 지정됩니다 $0. 이는로 모든 위치 매개 변수에 액세스하려고하면 $@시작 $1및 시작하는 매개 변수 만 가져 옵니다. 이것이 Dominik의 답변에 _매개 변수를 채우는 더미 인수 인을 갖는 이유 $0이므로 원하는 모든 인수는 $@예를 들어 매개 변수 확장을 사용 하거나 해당 루프에서 for 루프를 사용하면 나중에 사용할 수 있습니다 .

물론 허용 된 답변과 마찬가지로 bash -c 'shell_function $0 $@'명시 적으로 전달하여 작동 $0하지만 다시 $@예상대로 작동하지 않는다는 것을 명심해야합니다 .


0

직접적으로는 아닙니다. 찾기는 쉘이 아닌 별도의 프로세스에서 실행됩니다.

함수와 동일한 작업을 수행하는 쉘 스크립트를 작성하고 찾으십시오 -exec.


~ / bin에서 더 많은 파일을 피하려고했습니다. 그래도 고마워!
alxndr

-2

나는 -exec완전히 사용하지 않을 것 입니다. 왜 사용하지 xargs않습니까?

find . -name <script/command you're searching for> | xargs bash -c

당시 IIRC는 사용 된 자원의 양을 줄이려고 시도했습니다. 수백만 개의 빈 파일을 찾아 삭제 해보십시오.
alxndr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.