0

我遇到了困扰我一整天的大问题。

我有许多函数文件,它们具有基于用途的各种功能,即。SSH、日志记录等。

我有一个脚本,可以在其中将错误添加到全局错误数组中。我有一个错误陷阱,它调用另一个“exit_functions”文件中的脚本。

在这个 exit_functions 文件中,我做了很多事情,但我遇到问题的部分是一个循环,它运行这个数组并拉出实际上是命令的数组元素。这些命令是函数文件之一中的函数,这些函数文件已包含在调用脚本中,使用普通的 . /path/to/functions_file 语法。

我的所有其他功能(例如日志记录等)都在 exit_functions 文件中工作,只是这些特定功能无法调用。我收到一个错误:

/functions/exit_functions: line 109: closeSSHTunnel /tmp/ssh_tunnel_iA0yj.lck: No such file or directory

现在,killSSHTunnel 是一个函数,它位于以前包含的函数文件中,就像包含其他函数文件一样。我只使用数组中的这些函数调用得到这个错误。

我不确定我是否正确描述了它 - 如果我可以提供任何其他信息,请告诉我。


编辑下面的完整功能代码:

/functions/exit_functions 文件: exitTrap (){ local LCL_SCRIPT_ERROR

    if [ $ERR_COUNT_EXIT_FUNCT -gt 0 ]; then
        for LCL_SCRIPT_ERROR in "${ERROR_ARRAY[@]}"; do
            case $LCL_SCRIPT_ERROR in
                SVN_IMPORT      )   # match the SVN_IMPORT error name and search for specific commands to run
                                    exitMsg "$LCL_SCRIPT_ERROR; 111"
                                    executeErrorActions $LCL_SCRIPT_ERROR

                                    exitScript 111
                                    ;;

                *               )   exitMsg "$LCL_SCRIPT_ERROR; 255"
                                    exitScript 255
                                    ;;
            esac
        done
    fi
}

所以我的测试代码引入了一个错误状态,最终调用了上面的函数(exitTrap)。它已经填充了一个名为 ERROR_ARRAY 的数组。

因此,executeErrorActions 函数(也在 /functions/exit_functions 中)循环关联数组 ERROR_ACTION_ARRAY,寻找一个名为(在此测试中)SVN_IMPORT的键。当它找到这个键时,它正在寻找一个以 ':' 分隔的字符串。每个分隔部分将是一个单独的命令,当出现这种错误情况时我要运行它。在我的测试中,我有一个简单的条目(没有分隔符): *closeSSHTunnel /tmp/ssh_tunnel_iA0yj.lck*

现在,使用下面的函数,当我包含 IFS= 和未设置 IFS 之间的部分时,它可以正常工作,而是只使用下面有双注释哈希的单行。一旦我引入 IFS 分隔符来分解字符串,我就会得到错误:

/functions/exit_functions:第 109 行:closeSSHTunnel /tmp/ssh_tunnel_iA0yj.lck:没有这样的文件或目录

executeErrorActions (){
    if [ ! $# -eq 1 ]; then
        return 1
    fi

    local ERROR_NAME=$1
    local ERROR_ACTION

    #ERROR_KEY="SVN_IMPORT"

    for ERROR_ACTION in ${!ERROR_ACTION_ARRAY[@]}; do
        #echo KEY: $i VALUE: ${ERROR_ACTION_ARRAY[$i]}
        if [ $ERROR_NAME == $ERROR_ACTION ]; then

            ##$(${ERROR_ACTION_ARRAY[$ERROR_ACTION]})

            IFS=":"
            for i in ${ERROR_ACTION_ARRAY[$ERROR_ACTION]}; do
                $i
            done
            unset IFS
            return 0
        fi
    done
}

这是使用两个“:”分隔的命令、上面相同的closeSSHTunnel函数和一个简单的ls -la调用的测试运行的 set -x 输出。我使用 addError 函数调用将这些添加到 ERROR_ACTION_ARRAY 中,如下所示:

addError SVN_IMPORT closeSSHTunnel $SSH_TUNNEL_LCK_FILE:ls -la

这是 set -x 输出:

+ executeErrorActions SVN_IMPORT
+ '[' '!' 1 -eq 1 ']'
+ local ERROR_NAME=SVN_IMPORT
+ local ERROR_ACTION
+ for ERROR_ACTION in '${!ERROR_ACTION_ARRAY[@]}'
+ '[' SVN_IMPORT == SVN_IMPORT ']'
+ IFS=:
+ for i in '${ERROR_ACTION_ARRAY[$ERROR_ACTION]}'
+ 'closeSSHTunnel /tmp/ssh_tunnel_iA0yj.lck'
/functions/exit_functions: line 116: closeSSHTunnel /tmp/ssh_tunnel_iA0yj.lck: No such file or directory
+ for i in '${ERROR_ACTION_ARRAY[$ERROR_ACTION]}'
+ 'ls -la'
/functions/exit_functions: line 116: ls -la: command not found
+ unset IFS
+ return 0

请注意其中一个说“没有这样的文件或目录”,而另一个说“找不到命令”。现在是第 2 天,试图解决这个问题!

我的结论是 IFS 更改和字符串分解导致问题。

编辑 2

如果我稍微移动 IFS 调用并在循环内的 ':' 字符上终止 IFS,然后在执行调用后再次建立它,一切正常!!!!!!

for ERROR_ACTION in ${!ERROR_ACTION_ARRAY[@]}; do
        #echo KEY: $i VALUE: ${ERROR_ACTION_ARRAY[$i]}
        if [ $ERROR_NAME == $ERROR_ACTION ]; then
            #$(${ERROR_ACTION_ARRAY[$ERROR_ACTION]})
            IFS=$':'
            for i in ${ERROR_ACTION_ARRAY[$ERROR_ACTION]}; do
                unset IFS
                $i
                IFS=$':'
            done
            unset IFS
            return 0
        fi
done

谁能解释一下?

4

2 回答 2

1

这是因为您将 IFS 设置为“:” - bash 使用 IFS 将变量拆分为单词(即函数名称与其参数),而不是单独的命令。因此,它试图运行一个名为 的命令closeSSHTunnel /tmp/ssh_tunnel_iA0yj.lck,而不是一个以closeSSHTunnel参数命名的命令/tmp/ssh_tunnel_iA0yj.lck

$ ERROR_ACTION="echo something"
$ $ERROR_ACTION 
something
$ IFS=":"
$ $ERROR_ACTION 
-bash: echo something: command not found
$ ERROR_ACTION="echo:something"
$ $ERROR_ACTION 
something

如果您希望能够包含多个命令,则必须自己拆分它们。

编辑:鉴于更完整的脚本摘录,您需要在for拆分命令时将 IFS 设置为“:”,但在执行命令时恢复正常:

...
saveIFS="$IFS"
IFS=":"
for i in ${ERROR_ACTION_ARRAY[$ERROR_ACTION]}; do
    IFS="$saveIFS"
    $i
done
IFS="$saveIFS"

这对 IFS 的重置超过了严格必要的程度,但这样做比添加逻辑来确定何时需要重置更容易(顺便说一句,最终重置存在,以防循环永远不会执行)。

于 2013-03-20T14:21:41.297 回答
0

我不知道这是否是正确的方法,但我用 IFS=":" 遍历字符串并将其放入数组中,然后重置 IFS。然后我遍历该命令数组来执行它们。都在工作。

executeErrorActions (){
    if [ ! $# -eq 1 ]; then
        return 1
    fi

    local ERROR_NAME=$1
    local ERROR_ACTION
    local ACTION_ARRAY
    local ACTION

    if [ ${#ERROR_ACTION_ARRAY[@]} -gt 0 ]; then
        for ERROR_ACTION in ${!ERROR_ACTION_ARRAY[@]}; do
            if [ $ERROR_NAME == $ERROR_ACTION ]; then
                if [ ${#ERROR_ACTION_ARRAY[$ERROR_ACTION]} -gt 0 ]; then
                    IFS=$':'
                    ACTION_ARRAY=( ${ERROR_ACTION_ARRAY[$ERROR_ACTION]} )
                    unset IFS
                    for ACTION in ${!ACTION_ARRAY[@]}; do
                        exitMsg "Running action for $ERROR_ACTION"
                        ${ACTION_ARRAY[$ACTION]}
                        if [ $? -eq 0 ]; then
                            exitMsg "Error action succeeded"
                        else
                            exitMsg "Error action failed"
                            return 1
                        fi
                    done
                    return 0
                else
                    return 1    #no contents in the array element; no actions to run; maybe there should have been; return error
                fi
            else
                return 0    #no matching actions for this error; still return success
            fi
        done
    else
        return 0    #action array is empty
    fi
}
于 2013-03-21T03:04:43.953 回答