저는 Solaris 10을 사용하고 있으며 ksh(88), bash(3.00) 및 zsh(4.2.1)를 사용하여 다음을 테스트했습니다.
다음 코드는 결과를 생성하지 않습니다.
function foo {
echo "Hello World"
}
find somedir -exec foo \;
find는 여러 파일과 일치하며(대체로 표시됨 -exec ...
) -print
함수는 find
외부에서 호출될 때 완벽하게 작동합니다.
man find
이 페이지의 내용은 다음과 같습니다 -exec
.
-exec 명령 실행된 명령이 다음을 반환하면 참입니다. 0 값은 종료 상태로 사용됩니다. 끝 명령은 이스케이프 문자로 구분되어야 합니다. 세미콜론(;). 명령 매개변수 {}는 현재 경로 이름으로 바꾸십시오. 만약에 -exec의 마지막 인수는 {}이며 세미콜론(;) 대신 +를 지정하세요. 명령이 덜 자주 호출됩니다. {}는 경로 이름 그룹으로 대체됩니다. 만약에 이 명령을 호출하면 반환됩니다. 종료 상태로 0이 아닌 값을 찾으십시오. 0이 아닌 종료 상태를 반환합니다.
아마도 다음과 같이 할 수 있을 것입니다.
for f in $(find somedir); do
foo
done
하지만 필드 구분 기호 문제를 다루는 것이 두렵습니다.
호출에서 쉘 함수를 호출하는 것이 가능합니까(동일한 스크립트에 정의되어 있으므로 범위 문제에 대해 걱정할 필요가 없습니다) find ... -exec ...
?
나는 둘 다 시도했고 /usr/bin/find
같은 /bin/find
결과를 얻었습니다.
답변1
A function
는 셸 고유의 함수이므로 find -exec
사용하기 전에 셸을 생성하고 해당 셸에서 함수를 정의해야 합니다. 그것은 다음과 같습니다:
find ... -exec ksh -c '
function foo {
echo blan: "$@"
}
foo "$@"' ksh {} +
bash
환경을 통해 기능을 내보낼 수 있으므로 export -f
(bash에서) 다음을 수행할 수 있습니다.
foo() { ...; }
export -f foo
find ... -exec bash -c 'foo "$@"' bash {} +
ksh88
해당 함수는 (환경을 통하지 않고) 내 보내야 typeset -fx
하지만 실행 중인 she-bang less 스크립트에서만 사용할 수 있으므로 ksh
사용할 수 없습니다 ksh -c
.
또 다른 옵션은 다음을 수행하는 것입니다.
find ... -exec ksh -c "
$(typeset -f foo)"'
foo "$@"' ksh {} +
즉, 인라인 스크립트에서 함수 typeset -f
정의를 덤프하는 데 사용됩니다. 다른 기능을 사용하는 경우 해당 기능도 덤프해야 합니다.foo
foo
ps -f
또는 명령줄이 아닌 환경 변수를 통해 함수 정의를 전달할 수 있습니다(예: 출력에 표시됨).
FUNCDEFS=$(typeset -f foo) find ... -exec ksh -c '
eval "$FUNCDEFS" &&
unset -v FUNCDEFS &&
foo "$@"' ksh {} +
( unset -v FUNCDEFS
이 기능으로 실행되는 명령의 환경을 오염시키지 않기 위해 foo
(있는 경우))
답변2
\0을 구분 기호로 사용하고 생성된 명령의 파일 이름을 다음과 같이 현재 프로세스로 읽어옵니다.
foo() {
printf "Hello {%s}\n" "$1"
}
while IFS= read -d '' -r filename; do
foo "${filename}" </dev/null
done < <(find . -maxdepth 2 -type f -print0)
여기서 무슨 일이 일어나고 있나요?
read -d ''
다음 \0바이트까지 읽으므로 파일 이름의 이상한 문자에 대해 걱정할 필요가 없습니다.- 마찬가지로
-print0
생성된 각 파일 이름을 종료하려면 \n 대신 \0을 사용합니다. - 옵션
-r
(원시)은 파일 이름에서 백슬래시가 이스케이프되는 것을 방지합니다. cmd2 < <(cmd1)
cmd2 와 동일cmd1 | cmd2
하지만 cmd2는 하위 쉘 대신 기본 쉘에서 실행됩니다.- foo에 대한 호출은
/dev/null
실수로 파이프에서 읽히지 않도록 리디렉션됩니다. $filename
인용되어 있으므로 쉘은 공백이 포함된 파일 이름을 분할하려고 시도하지 않습니다.IFS=
Bash가 파일 이름에서 후행 공백을 제거하지 못하도록 방지합니다(감사합니다다니엘 스메드가드 부스이 수정을 위해)
이제 zsh, bash 및 ksh 93u read -d
에서는 <(...)
이전 ksh 버전이 확실하지 않습니다.
답변3
사전 정의된 셸 기능을 사용하기 위해 스크립트에서 생성된 하위 프로세스를 원하는 경우 이를 내보내야 합니다.export -f <function>
참고: export -f
bash에만 해당
단 하나뿐이니까껍데기실행할 수 있습니다껍데기기능:
find / -exec /bin/bash -c 'function "$1"' bash {} \;
편집: 기본적으로 스크립트는 다음과 같아야 합니다.
#!/bin/bash
function foo() { do something; }
export -f foo
find somedir -exec /bin/bash -c 'foo "$0"' {} \;
답변4
항상 적용되는 것은 아니지만 적용되는 경우 간단한 해결책입니다. 이 globstar
옵션을 설정합니다( set -o globstar
ksh93에서는 shopt -s globstar
bash ≥4, zsh에서는 기본적으로 활성화됨). 그런 다음 재귀를 사용하여 **/
현재 디렉터리와 해당 하위 디렉터리를 일치시킵니다.
예를 들어 find . -name '*.txt' -exec somecommand {} \;
다음 대신 실행할 수 있습니다.
for x in **/*.txt; do somecommand "$x"; done
대신 find . -type d -exec somecommand {} \;
다음을 실행할 수 있습니다.
for d in **/*/; do somecommand "$d"; done
대신 find . -newer somefile -exec somecommand {} \;
다음을 실행할 수 있습니다.
for x in **/*; do
[[ $x -nt somefile ]] || continue
somecommand "$x"
done
이것이 작동하지 않는 경우 **/
(쉘에 해당 기능이 없거나 find
쉘 아날로그가 없는 옵션이 필요하기 때문에),find -exec
매개변수에서 함수 정의.