使用 printf 捕获返回代码并排序数组

使用 printf 捕获返回代码并排序数组

我有一些当前可以正常工作的 bash 代码。然而,语法有点冗长,我将不胜感激任何有关如何缩短以下 bash 代码的建议。

如需完整参考,可以在此处找到整个脚本:https://gist.github.com/sacvalleytech/951f9eb98625f983f8f4dab623f5918b

# find new deps
# $1 -> file to search for dependencies
# $2 -> array of previous dependency changes (from siblings; used to invalidate cache); code not shown
# $3 -> hierarchy of dependencies (for circular ref checking); code not shown
DEP_OUT=( "$(changes "$DEP" "${DEP_OUT[*]}" "${DEP_HIERARCHY[*]}")" )

# >>> its really annoying to do this 
# >>> to capture the return code before sorting the array
# >>> sorting the array BEFORE causes the return code to be lost
[ "$?" -eq 0 ] && DEP_CHANGED=true

# sort values | normalize paths | uniq values
DEP_OUT=( $(printf "%s\n" "${DEP_OUT[@]}" | sort | trim | uniq) )

更新:

根据评论,我提出了以下解决方案。

function format_array {
    if [ -t 0 ]; then
        local ARRAY_IN=( $@ )
    else
        readarray ARRAY_ARGS < /dev/stdin
        local ARRAY_IN=( $@ ${ARRAY_ARGS[@]} )
    fi

    echo $(printf "%s\n" "${ARRAY_IN[@]}" | sort | normalize_paths | uniq)
}

shopt -s lastpipe
shopt -so pipefail

DEP_OUT=( $(changes "$DEP" "${DEP_OUT[*]}" "${DEP_HIERARCHY[*]}" | format_array "${DEP_OUT[*]}") ) && \
DEP_CHANGED=true

这对于传播正确的返回代码至关重要。

(我认为其中一条评论简短地提到了这一点,它看起来已被删除)

shopt -s lastpipe
shopt -so pipefail

答案1

您想要简化递归函数内使用的代码。

您想要改进的部分是捕获函数的退出代码。

您应该认识到返回码是不是传递信息的唯一途径。事实上,传输信息最快的方式是使用变量。在递归函数中,它应该是一个全局变量(不是用局部变量定义的),以便父级可以读回该值。在本例中,将返回码设置为 1 的行是

# return 1 (false) if $DEP_CHANGED = false && $DEP_FILE is cached
[ "$DEP_CHANGED" = false ] && (cached "$(dirname "$DEP_FILE")") && return 1

可以改为:

# Inform that data didn't change
[ "$DEP_CHANGED" = false ] && 
    (cached "$(dirname "$DEP_FILE")") && 
    dep_changed_in_function=false

并且,相应地:

for DEP in "${DEP_LIST[@]}"; do
    dep_changed_in_function=true

    # find new deps
    DEP_OUT=( "$(changes "$DEP" "${DEP_OUT[*]}" "${DEP_HIERARCHY[*]}")" )
    DEP_OUT=( $(printf "%s\n" "${DEP_OUT[@]}" | sort | trim | uniq) )

    # set $DEP_CHANGED flag
    [ "$dep_changed_in_function" -eq true ] && DEP_CHANGED=true
done

但是,如果 的数据输出已经排序,DEP_OUT则可以轻松避免第二次调用处理数组。changes

这可以通过以下方式轻松完成:

echo $(printf '%s\n' "${DEP_OUT[@]}" | sort | trim | uniq )

代替

echo "${DEP_OUT[@]}"

这样,您问题中的代码部分就减少为:

for DEP in "${DEP_LIST[@]}"; do
    dep_changed_in_function=true

    DEP_OUT=( "$(changes "$DEP" "${DEP_OUT[*]}" "${DEP_HIERARCHY[*]}")" )

    # set $DEP_CHANGED flag
    DEP_CHANGED=$dep_changed_in_function
done

当然,dep_changed_in_function=true可以在函数开始时设置。

答案2

以前见过这个。是的,这有点烦人。
诀窍是在执行任何其他可能改变返回代码的命令之前获取更改的返回代码。

我会尝试这样的事情:
DEP_OUT=( "$(changes "$DEP" "${DEP_OUT[*]}" "${DEP_HIERARCHY[]}")"; rc=$? )
或者
DEP_OUT=( "$(changes "$DEP" "${DEP_OUT[*]}" "${DEP_HIERARCHY[
]}")" );rc=$?

然后使用 $rc 代替 $?
[ $rc -eq 0 ] && DEP_CHANGED=true

如果这有效,那么您就可以将所有内容组合起来。
DEP_OUT=( "$(changes "$DEP" "${DEP_OUT[*]}" "${DEP_HIERARCHY[*]}";rc=$? )" | sort | trim | uniq)

请注意,我在 3 个不同的位置放置了 rc=$?。取决于您的 shell、操作系统、实例化等。您可能需要尝试才能找到放置它的确切位置。

相关内容