3

我正在寻找正确的方法来查看是否定义了函数。一种符合 POSIX 标准的方式。

__function_defined() {
    FUNC_NAME=$1
    d=$(declare -f $FUNCNAME)

    if [ "${DISTRO_NAME_L}" = "centos" ]; then
        if typeset -f $FUNC_NAME &>/dev/null ; then
            echo " * INFO: Found function $FUNC_NAME"
            return 0
        fi

    # Try POSIXLY_CORRECT or not
    elif test -n "${POSIXLY_CORRECT+yes}"; then
        if typeset -f ${FUNC_NAME} >/dev/null 2>&1 ; then
            echo " * INFO: Found function $FUNC_NAME"
            return 0
        fi
    else
        # Arch linux seems to fall here
        if $( type ${FUNC_NAME}  >/dev/null 2>&1 ) ; then
            echo " * INFO: Found function $FUNC_NAME"
            return 0
        fi
    echo " * INFO: $FUNC_NAME not found...."
    return 1
}

根据 debian 的checkbashisms脚本,以上所有内容都被视为 bash'isms。

尝试 grep 脚本也不行。例如:

    if [ "$(grep $FUNC_NAME $(dirname $0)/$(basename $0))x" != "x" ]; then
        # This is really ugly and counter producing but it was, so far, the
        # only way we could have a POSIX compliant method to find if a function
        # is defined within this script.
        echo " * INFO: Found function $FUNC_NAME"
        return 0
    fi

不起作用,因为脚本也应该像这样工作:

wget --no-check-certificate -O - http://URL_TO_SCRIPT | sudo sh

那么,什么是正确的、符合 POSIX 标准的方法呢?

请纯 sh,不要 bash,不要 ksh,不要其他 shell,只需纯 sh。哦,而且实际上也没有尝试运行该功能:)

可能吗?

我找到了一个符合 POSIX 的解决方案:

if [ "$(command -v $FUNC_NAME)x" != "x" ]; then
    echo " * INFO: Found function $FUNC_NAME"
    return 0
fi

现在的问题是,有更好的解决方案吗?

4

4 回答 4

2

我找到了一种符合 POSIX 的方式

if [ "$(command -v $FUNC_NAME)x" != "x" ]; then
    echo " * INFO: Found function $FUNC_NAME"
    return 0
fi

现在的问题是,有更好的解决方案吗?

于 2013-01-22T20:30:49.653 回答
1

这个答案不是 POSIX,因此不会与公认的答案竞争,但是当您只需要特定外壳的东西时,它可以是 FWIW/TDIL。

zsh你可以做到test "$(whence -w name)" = 'name: function'

bash你可以做到test "$(type -t name)" = 'function'

额外详情bash

function fn () {
  echo 'my function'
}

if test "$(type -t fn)" = 'function'; then
  echo 'fn is a function'
else
  echo 'fn is not a function'
fi

if test "$(type -t missing-fn)" = 'function'; then
  echo 'missing-fn is a function'
else
  echo 'missing-fn is not a function'
fi

help您可以通过输入提示符,然后输入help type和来了解有关 bash bultins 的更多信息help test

type -t name还可以判断是否name是各种事物,包括内置函数、文件或演示的函数。

如果您只关心变量或函数是否存在,您可以使用:

if test -v name; then
  echo "name is present: $name"
else
  echo "name is missing"
fi
于 2021-09-02T20:05:39.970 回答
1

以下是 POSIX 兼容的,应该可以在任何地方工作:

checkfunc() {
   case "$(type "$1" 2>/dev/null | head -n1)" in
   *" function"|*" function from zsh") return 0; ;;
   esac
   return 1
}
# Tested with:
# My shell is bash
script="$(declare -f checkfunc)"'
if checkfunc checkfunc; then :; else echo $0:error:not function; exit 1; fi
if checkfunc ls; then echo $0:error:executable; exit 1; fi
if checkfunc type; then echo $0:error:builtin; exit 1; fi
if checkfunc does-not-exists; then echo $0:error:not-exists; exit 1; fi
'
docker run --rm bash bash -c "$script"
docker run --rm alpine ash -c "$script"
docker run --rm ubuntu dash -c "$script"
docker run --rm alpine ash -c 'apk add mksh >/dev/null && mksh -c "$1"' - "$script"
docker run --rm alpine ash -c 'apk add zsh >/dev/null && zsh -c "$1"' - "$script"

如果我可以说,我会说我也可以拥有 SVR4 的源代码并进行了修补ksh,因此它可以与当前的 GCC 编译并在那里进行测试,并且type可以在<xxx> is a function那里输出,就像ash.

zsh碰巧输出<func> is a shell function from zsh,所以我决定from zsh专门处理。

于 2021-09-02T21:48:50.597 回答
-1

虽然我能理解您为什么会这样想,但从另一个位置动态拉取您的脚本对于在运行它之前按您喜欢的方式解析(或“grepping”)它并没有很大的障碍。这样的事情可以通过多种方式完成,但是,据我所知,它们都需要一个基本的文件描述符来完成功能上的操作。

Bash 及其同类通过重定向的子壳命令为我们提供了透明文件描述符的便利,这些命令允许各种管道灵活性,如下所示:

eval "$(wget --no-check-certificate -O - ${_URL} | tee \
    >(read func; echo '_FUNC='"$(grep -Eo '^[^ |^#]*()' <(echo "${func}"))") \
    | sudo sh)" 

但是,正如已经提到的,POSIX 并未指定此类语法设备,因此您必须找到另一种方法。此外,虽然它可能更短,但这可能会有点令人困惑。可能有更好的方法可以做到这一点,但我想它们至少需要 2 或 3 个子外壳级别。(不过,以其他方式显示肯定会很酷。)

然而,POSIX 确实指定的是非常有用的heredoc. 在heredoc可声明的 POSIX 变量语法类型中很少见,不仅因为它可以整齐地跨越多行而不会出现转义错误,或者因为它允许通过简单地引用它的终止符来干净地定义 shell 替换,但最重要的是因为它描述了一个文件类型句柄。的这种质量heredoc经常被忽视,也许是因为 Bash 和 co 已经在很大程度上消除了它的重要性一段时间,但每次我们遵守以heredoc“文件结尾”首字母缩略词结尾的约定时,我们都应该提醒它EOF

为了拉入脚本文件,以编程方式对其进行修改,然后使用 shell 解释器执行它,您将需要一个文件描述符。当然,您可以读写/tmp/以允许这样做,但我通常会尽量避免这种情况,因为它只是感觉草率。如果必须,你甚至可以重写自己,但这可能不是最好的习惯。但是,如果您发现这样做的真正原因,最安全的方法可能是在脚本开头的函数中定义重写代码,并且只在最后调用它,这样您就可以确保整个文件已完全加载进入记忆。像这样的东西:

_rewrite_me() {printf %s "${1}" > ${0} && exec ${0}} 
#SHELL CODE
#MORE 
_NEW_ME="$(printf %s "${_MORE_SHELL_CODE}" | cat <${0})"
rewrite_me ${_NEW_ME}

尽管如此,我还是最喜欢heredoc最好的,我认为这是本案中缺失的拼图。以下是使用heredoc.

#!/bin/sh
_URL='http://URL/TO/SCRIPT'

_SCRIPT="$(wget --no-check-certificate -O - ${_URL} |\
    grep -Ev '^#!')" 

_FUNC="$(sed -n 's:^\([^ |^#|^=]*\)().*{.*$:\1:p' <<_EOF_)"
${_SCRIPT}
_EOF_
echo "${_FUNC}"

sh <<_EOF_
${_SCRIPT}
_EOF_
于 2013-11-10T02:57:01.077 回答