bash에 함수가 존재하는지 확인


187

현재 bash에서 실행되는 단위 테스트를 수행하고 있습니다. 단위 테스트는 bash 스크립트에서 초기화, 실행 및 정리됩니다. 이 스크립트는 보통 init (), execute () 및 cleanup () 함수를 포함합니다. 그러나 필수 사항은 아닙니다. 그것들이 정의되어 있는지 여부를 테스트하고 싶습니다.

나는 이전에 소스를 greping하고 seding 하여이 작업을 수행했지만 잘못된 것 같습니다. 더 우아한 방법이 있습니까?

편집 : 다음 코드는 매력처럼 작동합니다.

fn_exists()
{
    LC_ALL=C type $1 | grep -q 'shell function'
}

감사. 나는 이것을 사용하여 쉘 라이브러리를로드 할 때 스텁 아웃 된 함수 버전을 조건부로 정의했습니다. fn_exists foo || foo() { :; }
Harvey

2
type -t및 을 사용하여 grep을 저장할 수 있습니다 ==.
Roland Weber

로케일이 영어가 아닌 경우 작동하지 않습니다. 핀란드어 로캘을 사용할 때 와 독일어를 사용할 때를 type test_function말합니다 . test_function on funktio.ist eine Funktion
Kimmo Lehto

3
영어 이외의 로케일에 대한 LC_ALL=Cresque에
gaRex

답변:


191

나는 당신이 'type'명령을 찾고 있다고 생각합니다. 어떤 것이 함수인지, 내장 함수인지, 외부 명령인지 아니면 정의되지 않았는지 알려줍니다. 예:

$ LC_ALL=C type foo
bash: type: foo: not found

$ LC_ALL=C type ls
ls is aliased to `ls --color=auto'

$ which type

$ LC_ALL=C type type
type is a shell builtin

$ LC_ALL=C type -t rvm
function

$ if [ -n "$(LC_ALL=C type -t rvm)" ] && [ "$(LC_ALL=C type -t rvm)" = function ]; then echo rvm is a function; else echo rvm is NOT a function; fi
rvm is a function

120
type -t $function식사권입니다.
Allan Wind

4
왜 답변으로 게시하지 않았습니까? :-)
terminus

내가 먼저 선언을 사용하여 답변을 게시했기 때문에 :-)
Allan Wind

5
type [-t]하기 좋은 말할 일이 무엇인지,하지만 때 테스트 뭔가가 함수 인 경우 서브 프로세스를 생성 둘 다 사용 역 따옴표, 또는 grep으로하는 파이프이 있기 때문에, 그것은 느린.
Lloeki

1
내가 잘못 읽지 않는 한 type 을 사용 하면 일치하는 파일이 있는지 확인하기 위해 최소한의 액세스를 수행해야합니다. @Lloeki, 당신은 정확하지만 최소한의 출력을 생성하는 옵션이며 여전히 오류 수준을 사용할 수 있습니다. 하위 프로세스없이 결과를 얻을 수 있습니다 type -t realpath > /dev/shm/tmpfile ; read < /dev/shm/tmpfile( 예 : (나쁜 예)). 그러나 디스크 디스크 io가 0이므로 선언 이 가장 좋습니다.
Orwellophile

79
$ g() { return; }
$ declare -f g > /dev/null; echo $?
0
$ declare -f j > /dev/null; echo $?
1

1
나를 위해 굉장히 일했다. 특히 쉘에 유형에 대한 -t 플래그가 없기 때문에 ( "$ command"유형에 많은 문제가있었습니다)
Dennis

2
실제로, 그것은 또한 zsh (rc 스크립트에 유용)에서도 작동하며 타입을 grep 할 필요가 없습니다.
Lloeki '12

2
@DennisHodapp 필요 없음 type -t, 대신 종료 상태에 의존 할 수 있습니다. 나는 type program_name > /dev/null 2>&1 && program_name arguments || echo "error"무언가를 부를 수 있는지를 오랫동안 보았습니다. 분명히 type -t위와 위의 방법을 사용하면 "호출 가능"여부 만이 아니라 유형을 감지 할 수 있습니다.
0xC0000022L

program_name이 함수가 아닌 경우 어떻게해야합니까?
David Winiecki

2
@ 0xC0000022L 나는 종료 상태를 사용하는 것이 program_name이 함수인지 알려주지 않는 방법에 대해 nitpicking하고 있었지만 이제는 "명백하게 유형 -t이고 위의 방법으로 유형을 감지 할 수 있다고 말할 때 "호출 가능 여부"만이 아닙니다. " 죄송합니다.
David Winiecki

40

선언이 테스트보다 10 배 빠르면 확실한 대답이 될 것입니다.

편집 : 아래에서 -f옵션은 BASH와 함께 불필요하므로 자유롭게 남겨 두십시오. 개인적으로 어떤 옵션이 어떤 옵션을 수행하는지 기억하는 데 어려움이 있으므로 둘 다 사용합니다. -f 는 함수를 나타내고 -F 는 함수 이름을 나타냅니다.

#!/bin/sh

function_exists() {
    declare -f -F $1 > /dev/null
    return $?
}

function_exists function_name && echo Exists || echo No such function

선언하는 "-F"옵션을 사용하면 전체 내용이 아니라 찾은 함수의 이름 만 반환합니다.

/ dev / null을 사용할 때 측정 가능한 성능 저하가 없어야하며, 그렇게 많이 걱정하는 경우 :

fname=`declare -f -F $1`
[ -n "$fname" ]    && echo Declare -f says $fname exists || echo Declare -f says $1 does not exist

또는 무의미한 즐거움을 위해 두 가지를 결합하십시오. 그들은 둘 다 작동합니다.

fname=`declare -f -F $1`
errorlevel=$?
(( ! errorlevel )) && echo Errorlevel says $1 exists     || echo Errorlevel says $1 does not exist
[ -n "$fname" ]    && echo Declare -f says $fname exists || echo Declare -f says $1 does not exist

2
'-f'옵션은 중복됩니다.
Rajish

3
-F옵션 데없는 (휴대 유용) zsh을 존재
Lloeki

-F또한 실제로 필요하지는 않습니다 : 함수 정의 / 본문 만 억제하는 것 같습니다.
blueyed

1
@blueyed 필요하지 않을 수도 있지만, 전체 내용을 나열하지 않고 함수가 존재하는지 확인하려고합니다. 파일을 사용하여 파일이 있는지 확인 하시겠습니까 cat "$fn" | wc -c? zsh의 경우, bash 태그가 당신에게 실마리가 없다면 질문 자체에 있어야 할 것입니다. "bash에 함수가 존재하는지 확인하십시오." 또한 -F옵션이 zsh에는 존재하지 않지만 오류가 발생하지 않으므로 -f와 -F를 모두 사용하면 zshbash 모두에서 검사가 성공할 수 있습니다. 그렇지 않으면 그렇지 않습니다. .
Orwellophile

@Orwellophile -F은 zsh에서 부동 소수점 숫자로 사용됩니다. -Fbash에서 왜 더 잘 사용하는지 알 수 없습니다 ! declare -fbash에서 리턴 코드와 같은 방식으로 작동 한다는 인상을 받았습니다 .
blueyed

18

다른 솔루션과 의견에서 빌려서, 나는 이것을 생각해 냈습니다.

fn_exists() {
  # appended double quote is an ugly trick to make sure we do get a string -- if $1 is not a known command, type does not output anything
  [ `type -t $1`"" == 'function' ]
}

로 사용 ...

if ! fn_exists $FN; then
    echo "Hey, $FN does not exist ! Duh."
    exit 2
fi

주어진 인수가 함수인지 확인하고 리디렉션 및 기타 grepping을 피합니다.


좋아, 그룹에서 내 즐겨 찾기! 인수에 큰 따옴표를 원하지 않습니까? 에서처럼[ $(type -t "$1")"" == 'function' ]
quickshiftin

감사합니다 @quickshiftin; 큰 따옴표를 원하는지 모르겠지만 .. 따옴표로 묶어야 할 이름으로 함수를 선언 할 수도 있지만 아마도 옳습니다.
Grégory Joseph

4
bash를 사용하고 있으며 [[...]]대신 [...]hack hack을 제거하십시오. 백틱 포크도 느리다. declare -f $1 > /dev/null대신 사용하십시오 .
Lloeki '12

3
빈 인수로 오류를 피하고 따옴표를 줄이고 '='posix 호환 평등을 사용하면 다음과 같이 안전하게 줄일 수 있습니다. fn_exists() { [ x$(type -t $1) = xfunction ]; }
qneill

10

오래된 게시물을 준공 ...하지만 최근에 이것을 사용하고 다음과 같이 설명 된 두 가지 대안을 테스트했습니다.

test_declare () {
    a () { echo 'a' ;}

    declare -f a > /dev/null
}

test_type () {
    a () { echo 'a' ;}
    type a | grep -q 'is a function'
}

echo 'declare'
time for i in $(seq 1 1000); do test_declare; done
echo 'type'
time for i in $(seq 1 100); do test_type; done

이 생성 :

real    0m0.064s
user    0m0.040s
sys     0m0.020s
type

real    0m2.769s
user    0m1.620s
sys     0m1.130s

선언은 helluvalot 더 빠릅니다!


1
grep없이 수행 할 수 있습니다. test_type_nogrep () { a () { echo 'a' ;}; local b=$(type a); c=${b//is a function/}; [ $? = 0 ] && return 1 || return 0; }
qneill

@qneill 나는 내 대답 에서 다소 광범위한 테스트를 수행 했다 .
jarno

파이프는 가장 느린 요소입니다. 이 테스트는 비교하지 않습니다 typedeclare. type | grep와 비교 합니다 declare. 이것은 큰 차이입니다.
kyb

7

출력 또는 종료 코드를 확인하기 위해 'declare'을 사용하는 것으로 요약됩니다.

출력 스타일 :

isFunction() { [[ "$(declare -Ff "$1")" ]]; }

용법:

isFunction some_name && echo yes || echo no

그러나 메모리가 제공되는 경우 null로 리디렉션하는 것이 출력 대체보다 빠릅니다 (즉, 끔찍하고 오래된`cmd` 메소드를 삭제하고 대신 $ (cmd)를 사용해야 함). 선언이 발견되면 true / false를 반환합니다. 찾을 수없고 함수는 함수에서 마지막 명령의 종료 코드를 반환하므로 일반적으로 명시적인 반환이 필요하지 않으며 오류 코드를 확인하는 것이 문자열 값 (널 문자열도 확인)보다 빠르기 때문에 :

종료 상태 스타일 :

isFunction() { declare -Ff "$1" >/dev/null; }

그것은 아마도 당신이 얻을 수있는 간결하고 양성에 관한 것입니다.


3
간결함을 극대화하기 위해isFunction() { declare -F "$1"; } >&-
Neil

3
isFunction() { declare -F -- "$@" >/dev/null; }내 추천입니다. 이름 목록에서도 작동하며 (모두 함수 인 경우에만 성공) 이름으로 시작하는 데 아무런 문제가 없으며 -내 측면 ( bash4.2.25) 에서는 이름을 쓸 수 없기 때문에 declare출력이 닫힐 때 항상 실패합니다. >&-이 경우 stdout
Tino

그리고 유의하시기 바랍니다, 그것은 echo때때로 일부 플랫폼의 "중단 된 시스템 호출"와 함께 실패 할 수 있습니다. 이 경우 여전히 출력 할 수 있습니다 "를 && 에코 예 || 에코 없음 확인"을 no경우 check사실이다.
티노

7

다른 솔루션 테스트 :

#!/bin/bash

test_declare () {
    declare -f f > /dev/null
}

test_declare2 () {
    declare -F f > /dev/null
}

test_type () {
    type -t f | grep -q 'function'
}

test_type2 () {
     [[ $(type -t f) = function ]]
}

funcs=(test_declare test_declare2 test_type test_type2)

test () {
    for i in $(seq 1 1000); do $1; done
}

f () {
echo 'This is a test function.'
echo 'This has more than one command.'
return 0
}
post='(f is function)'

for j in 1 2 3; do

    for func in ${funcs[@]}; do
        echo $func $post
        time test $func
        echo exit code $?; echo
    done

    case $j in
    1)  unset -f f
        post='(f unset)'
        ;;
    2)  f='string'
        post='(f is string)'
        ;;
    esac
done

출력 예 :

test_declare (f는 함수 임)

실제 0m0,055s 사용자 0m0,041s sys 0m0,004s 종료 코드 0

test_declare2 (f는 함수 임)

실제 0m0,042s 사용자 0m0,022s sys 0m0,017s 종료 코드 0

test_type (f는 함수 임)

실제 0m2,200s 사용자 0m1,619s sys 0m1,008s 종료 코드 0

test_type2 (f는 함수 임)

실제 0m0,746s 사용자 0m0,534s sys 0m0,237s 종료 코드 0

test_declare (설정 해제)

실제 0m0,040s 사용자 0m0,029s 시스템 0m0,010s 종료 코드 1

test_declare2 (설정 해제)

실제 0m0,038s 사용자 0m0,038s sys 0m0,000s 종료 코드 1

test_type (f 설정 해제)

실제 0m2,438s 사용자 0m1,678s sys 0m1,045s 종료 코드 1

test_type2 (설정 해제)

실제 0m0,805s 사용자 0m0,541s sys 0m0,274s 종료 코드 1

test_declare (f는 문자열)

실제 0m0,043s 사용자 0m0,034s sys 0m0,007s 종료 코드 1

test_declare2 (f는 문자열)

실제 0m0,039s 사용자 0m0,035s 시스템 0m0,003s 종료 코드 1

test_type (f는 문자열)

실제 0m2,394s 사용자 0m1,679s sys 0m1,035s 종료 코드 1

test_type2 (f는 문자열)

실제 0m0,851s 사용자 0m0,554s sys 0m0,294s 종료 코드 1

따라서 declare -F f가장 좋은 해결책 인 것 같습니다.


여기서주의 : declare -F ff가 zsh에 존재하지 않으면 0이 아닌 값을 반환하지 않지만 bash는 그렇습니다. 조심해서 사용하십시오. declare -f f다른 한편으로, stdout에 기능의 정의를 첨부하여 예상대로 작동합니다 (성 가실 수 있습니다 ...)
Manoel Vilela

1
당신이 노력이 test_type3 () { [[ $(type -t f) = function ]] ; }(비록 <10 %) 현지 VAR을 정의하는 한계 비용이
올리버

4

다른 답변에 대한 내 의견에서 (이 페이지로 돌아갈 때 계속 누락됩니다)

$ fn_exists() { test x$(type -t $1) = xfunction; }
$ fn_exists func1 && echo yes || echo no
no
$ func1() { echo hi from func1; }
$ func1
hi from func1
$ fn_exists func1 && echo yes || echo no
yes

3
fn_exists()
{
   [[ $(type -t $1) == function ]] && return 0
}

최신 정보

isFunc () 
{ 
    [[ $(type -t $1) == function ]]
}

$ isFunc isFunc
$ echo $?
0
$ isFunc dfgjhgljhk
$ echo $?
1
$ isFunc psgrep && echo yay
yay
$

2

나는 그것을 다음과 같이 향상시킬 것이다.

fn_exists()
{
    type $1 2>/dev/null | grep -q 'is a function'
}

그리고 이것을 다음과 같이 사용하십시오 :

fn_exists test_function
if [ $? -eq 0 ]; then
    echo 'Function exists!'
else
    echo 'Function does not exist...'
fi

2

이것이 존재하는지 알려 주지만 그것이 함수라는 것은 아닙니다

fn_exists()
{
  type $1 >/dev/null 2>&1;
}

2

나는 특히 Grégory Joseph의 솔루션을 좋아했습니다.

그러나 "큰 따옴표 못생긴 트릭"을 극복하기 위해 약간 수정했습니다.

function is_executable()
{
    typeset TYPE_RESULT="`type -t $1`"

    if [ "$TYPE_RESULT" == 'function' ]; then
        return 0
    else
        return 1
    fi
}

0

외부 명령없이 'type'을 사용할 수는 있지만 두 번 호출해야하므로 'declare'버전보다 약 두 배 느립니다.

test_function () {
        ! type -f $1 >/dev/null 2>&1 && type -t $1 >/dev/null 2>&1
}

또한 이것은 POSIX sh에서 작동하지 않으므로 퀴즈를 제외하고는 전혀 가치가 없습니다!


test_type_nogrep () {a () {echo 'a';}; 국부 b = $ (타입 a); c = $ {b //는 함수 /}입니다. [$? = 0] && 리턴 1 || 리턴 0; } – qneill
Alexx Roche
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.