符合POSIX的方法,以查看是否在sh脚本中定义了一个函数

时间:2013-01-21 21:29:41

标签: posix sh

我正在寻找是否定义函数的正确方法。符合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兼容方法是什么?

请原谅,没有bash,没有ksh,没有其他shell,只是简单的sh。哦,并且实际上没有尝试运行该功能:)

有可能吗?

我找到了符合POSIX标准的解决方案:

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

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

2 个答案:

答案 0 :(得分:1)

我找到了符合POSIX的方式

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

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

答案 1 :(得分:0)

虽然我可以理解你为什么这么认为,但是在你运行它之前动态地从另一个位置拉动你的脚本并不是解析(或“grepping”)它的绝佳障碍。这样的事情可以用几种方式完成,但是,据我所知,它们都需要一个基本的文件描述符来完成功能上的完整操作。

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

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

但是,正如已经提到的那样,POSIX没有指定这样的语法设备,因此您必须找到另一种方法。而且,尽管可能更短,但这可能有点令人困惑。可能有更好的方法可以做到这一点,但我想他们至少需要2或3个子壳级别。 (不过,以其他方式显示确实很酷。)

然而,POSIX指定的是非常有用的heredocheredoc在声明的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简短而简单的,应完全符合POSIX标准,并且应该很容易适应您的目的,我希望:

#!/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_