Bash中的尾递归

时间:2017-03-01 08:48:24

标签: bash recursion tail-recursion

在尝试使用该服务进行任何进一步更改之前,我已尝试编写脚本以验证指标的所有统计信息是否为正数。我坚持的部分是考虑如何为以下用例尾随递归:

function load_cache() {
    cacheStat=( $(curl -s -X GET "http://localhost:${MET_PORT}/metrics" | sed 's/\\\\\//\//g' | sed 's/[{}]//g' | awk -v k="cacheSize" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | sed 's/\"\:\"/\|/g' | sed 's/[\,]/ /g' | sed 's/\"//g' | grep -w "cacheSize" | cut -d ':' -f 2) )

    # the above gives me the ouput(cacheStat) as -
    # 2.0
    # 311.0
    # 102.0

    count=0
    for index in ${!cacheStat[*]}
    do
        if [[ ${cacheStat[$index]} -le 0 ] && [ $count -lt 3 ]]; then
            sleep .5
            count=$[$count +1];
            load_cache
            #Wouldn't the above initialise `count` to 0 again.
        fi
    done
}

我想要做的是,如果cacheStat中的任何元素小于或等于0,则休眠.5秒并再次查询cacheStat并对所有元素执行检查其元素又来了。虽然不是这样做的次数超过3次我试图使用`count。

打开任何改进脚本的建议。

更新 - 按照@Inian建议修改脚本

RETRY_COUNT=0
function load_cache() {
    cacheStat=( $(curl -s -X GET "http://localhost:${MET_PORT}/metrics" | sed 's/\\\\\//\//g' | sed 's/[{}]//g' | awk -v k="cacheSize" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | sed 's/\"\:\"/\|/g' | sed 's/[\,]/ /g' | sed 's/\"//g' | grep -w "cacheSize" | cut -d ':' -f 2) );
    for index in ${!cacheStat[*]}
    do
        echo "Stat - ${cacheStat[$index]}"
        if (( ${cacheStat[$index]} <= 0 )) && (( $RETRY_COUNT < 3 )); then
            echo "Attempt count - ${RETRY_COUNT}"
            sleep .5s
            RETRY_COUNT=$((RETRY_COUNT +1));
            load_cache
        fi
    done
}

日志读取 -

>     > + cacheStat=($(curl -s -X GET "http://localhost:${MET_PORT}/metrics" | sed 's/\\\\\//\//g' | sed
> 's/[{}]//g' | awk -v k="cacheSize"
>     > '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | sed
>     > 's/\"\:\"/\|/g' | sed 's/[\,]/ /g' | sed 's/\"//g' | grep -w
>     > "cacheSize" | cut -d ':' -f 2))
>     > ++ curl -s -X GET http://localhost:8181/metrics
>     > ++ sed 's/\\\\\//\//g'
>     > ++ sed 's/[{}]//g'
>     > ++ sed 's/[\,]/ /g'
>     > ++ awk -v k=cacheSize '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}'
>     > ++ sed 's/\"\:\"/\|/g'
>     > ++ cut -d : -f 2
>     > ++ sed 's/\"//g'
>     > ++ grep -w cacheSize

我猜它甚至都没有迭代。

2 个答案:

答案 0 :(得分:3)

通过将count=0移到函数体外部来删除无限递归。

此外,您的脚本还有一些问题,语法违规和过时的构造,行12-14应该是,

if [[ ${cacheStat[$index]} -le 0 ]] && [[ $count -lt 3 ]]; then
    sleep .5s
    count=$((count +1));
    load_cache
fi

或)在(())中使用更易读的算术运算符if-clause作为

if (( ${cacheStat[$index]} <= 0 )) && (( $count < 3 )); then
  

bash本身并不支持浮点运算(在您的情况下进行比较),使用第三方工具,例如bcawk

if (( $(echo "${cacheStat[$index]} <= 0" | bc -l) )) && (( $count < 3 )); then

答案 1 :(得分:1)

您可以使用JSON解析器来避免所有特殊的JSON解析。

# Avoid using Bash-only "function" keyword
load_cache () {
    local try
    for try in 1 2 3; do
        # Suction: jq doesn't return non-zero exit code for no match
        # work around that by piping to grep .
        if curl -s -X GET "http://localhost:${MET_PORT}/metrics" |
            jq '.[] | select(cacheSize < 0)' |
            grep .
        then
            # Notice also redirection to stderr for diagnostic messages
            echo "$0: Attempt $try failed, sleeping before retrying" >&2
            sleep 0.5
        else
            # Return with success, we are done, exit function
            return 0
        fi
    done

    # Return failure
    return 1
}

我认为没有理由宁可通过直接for循环来递归来控制重试次数。

如果您不想查看违规值,可以在条件中使用grep -q。如果您不想要输出,我希望您会load_cache >/dev/null

如果你想看到非违规的价值,代码将需要一些重构,但我专注于优雅而简洁地完成中心工作。这是一个草图,主要是为了向您展示jq语法。

load_cache () {
    local try
    local results
    for try in 1 2 3; do
        results=$(curl -s -X GET "http://localhost:${MET_PORT}/metrics" |
            jq '.[] | .cacheSize' | tr '\n' ' ')
        echo "$0: try $try: cacheSize $results" >&2
        # Funky: massage the expression we test againt into a normalized form
        # so that we know that the value will always be preceded by a space
        case " $results " in
          *" 0 "* | *" -"* )
             case $try in
              3) echo "$0: try $try failed; aborting" >&2 ;;
              *) echo "$0: try $try failed; sleeping before retrying" >&2
                 sleep 0.5 ;;
             esac;;
          *) return 0
        esac
    done
    return 1
}

嵌套case以避免在最后一次迭代中睡觉并不是特别优雅,但至少它应该确保读者清醒。 / -8