我有一些当前可以正常工作的 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、操作系统、实例化等。您可能需要尝试才能找到放置它的确切位置。