从Linux bash递归函数返回值

时间:2016-06-04 13:09:02

标签: linux bash recursion

我正在用我的bash脚本面对一些奇怪的行为。它基本上是一个脚本,如果它第一次失败,它会尝试多次ping远程主机。我这样做是为了排除任何虚假警报。我想我会通过编写一个自我调用并再次尝试ping的递归函数来快速实现这一点。

我的问题是返回的值。我发现该函数多次返回返回值,对应于递归的次数。这很奇怪。例如,在我的下面的代码中,ip_up()函数应该为远程主机向上返回1,为向下返回0。但是,当远程主机关闭时,该函数返回0两次,这对应于递归。

我的代码可能有什么问题,或者bash是如何工作的?

#!/bin/bash
    ip_up(){
            server_ip=$1
            trials=$2
            max_trials=2
            status=0
            echo "server ip is: $server_ip, trial $trials" >&2
            if ping -i 1 -c 3 "$server_ip" &> /dev/null
            then
                status=1
            else
                status=0
                while ((  "$trials" < "$max_trials" )); do
                    echo -e "$server_ip is down: Trial $trials, checking again after 1 sec" >&2
                    sleep 1
                    ((trials++))
                    ip_up "$server_ip" "$trials"
                done
            fi
            echo "$status"
    }

    status=$(ip_up "$ip" 1)
    echo -e "the returned status is: ====$status====\n"
   if [ "$server_status" -eq 0 ]; then
        msg="$timestamp: Server $hostname ($ip) is DOWN"; echo "$msg"
   fi

    <<'COMMENT'
    //results

     $ ./check_servers.sh
    checking box1(173.36.232.6)
    server ip is: 173.36.232.6, trial 1
    173.36.232.6 is down: Trial 1, checking again after 1 sec
    server ip is: 173.36.232.6, trial 2
    the returned status is: ====0
    0====

    ./check_servers.sh: line 41: [: 0
    0: integer expression expected
    Sat Jun  4 15:16:11 EAT 2016 box2 (173.36.232.7) is UP
    checking box2 (173.36.232.7)
    server ip is: 173.36.232.7, trial 1
    the returned status is: ====1====

    COMMENT

2 个答案:

答案 0 :(得分:2)

我无法想象很多情况下我会在循环中使用一秒延迟的代码,这足以让它值得写作函数 - 我使用相对直的 - 前进(迭代)脚本。但是,如果你确定这对你有益,那么将剧本变成一个功能是不可能的。你的情况与我的不同。

#!/bin/sh

[ $# = 1 ] || [ $# = 2 ] || { echo "Usage: $0 ip-address [max-trials]" >&2; exit 1; }
server_ip="$1"
maxtrials="${2:-2}"
trial=1

while echo "server: $server_ip, trial $trial" >&2
      ! ping -i 1 -c 3 "$server_ip" > /dev/null 2>&1 || exit 0
do
    trial=$(($trial + 1))
    [ "$trial" -gt "$maxtrials" ] && break
    echo "$0: $server_ip is down: checking again after 1 sec" >&2
    sleep 1
done

echo "$(date +'%Y-%m-%d %H:%M:%S'): Server $server_ip is DOWN"
exit 1

第一个代码块设置控件,默认为2次尝试。

while循环控件包含echo,然后尝试ping IP地址(或主机名)。如果命令成功(主机可ping),则! ping状态为false,因此执行|| exit 0,脚本以0状态退出,表示成功(主机可ping)。如果命令失败(主机不可ping),则! ping状态为true,因此不执行|| exit 0,并输入循环体。如果达到限制,它会递增试验编号并中断循环。否则,它会打印其消息并休眠并返回循环的开始。

只有在exit 0未执行的情况下才会触及结束广告块,因此ping失败且服务器已关闭&#39; (或不存在)。然后,您会收到一条带时间戳的消息,指示服务器已关闭,并以非零状态退出以指示失败。

可能还有其他无数种方法可以做到这一点。我可能与错误消息更加一致 - 例如,我可能会保存arg0="$(basename "$0" .sh)"然后使用$arg0作为所有消息的前缀(或者可能在时间戳之后添加它)。可以对此进行调整以报告服务器已启动。代码适用于POSIX shell,而不仅仅是Bash(所以dash接受它,例如Korn shell,但是Heirloom(Bourne)Shell并不是因为它不喜欢{ {1}}或$(…))。

还可以将其编写为一个简单的计数循环,用于测试$((…))的状态,退出成功,并进行报告和重试。但是,如果循环退出而没有对ping的值进行双重测试,则避免上一个sleep 1是很棘手的。这在运行时并不昂贵,但它是重复的源泉而且干 - 不要重复自己 - 这是一个值得追求的原则。

$trial

我并不完全热衷于此,但它适用于Bash和Korn shell。

将最后一个脚本转换为函数基本上是微不足道的 - 将#!/bin/bash [ $# = 1 ] || [ $# = 2 ] || { echo "Usage: $0 ip-address [max-trials]" >&2; exit 1; } server_ip="$1" maxtrials="${2:-2}" for ((trial = 1; trial <= maxtrials; trial++)) do echo "server: $server_ip, trial $trial" >&2 if ping -i 1 -c 3 "$server_ip" > /dev/null 2>&1 then exit 0 elif [ "$trial" -lt "$maxtrials" ] then echo "$0: $server_ip is down: checking again after 1 sec" >&2 sleep 1 fi done echo "$(date +'%Y-%m-%d %H:%M:%S'): Server $server_ip is DOWN" exit 1 语句更改为exit语句,并将函数的开头和结尾包装起来:

return

保存在#!/bin/bash function upip() { [ $# = 1 ] || [ $# = 2 ] || { echo "Usage: $0 ip-address [max-trials]" >&2; return 1; } server_ip="$1" maxtrials="${2:-2}" for ((trial = 1; trial <= maxtrials; trial++)) do echo "server: $server_ip, trial $trial" >&2 if ping -i 1 -c 3 "$server_ip" > /dev/null 2>&1 then return 0 elif [ "$trial" -lt "$maxtrials" ] then echo "$0: $server_ip is down: checking again after 1 sec" >&2 sleep 1 fi done echo "$(date +'%Y-%m-%d %H:%M:%S'): Server $server_ip is DOWN" return 1 } ,我读了函数:

upip-func.sh

答案 1 :(得分:1)

你的功能不是“返回”任何东西。它会向stdout输出一个值,每次调用都会这样做。

如果要使用此机制模拟函数返回,则需要捕获并重新发送该值:

Bash函数返回一个退出状态,这可以正常工作(只要你期望0成功)。如果未另行指定,则返回值为最后一个命令的值。所以以下方法可行:

tryn() {
  if (($1 == 0)); then return 2; fi
  "$@" || tryn $(($1-1)) "$@"
}

if tryn 2 ping $host; then
   # success
fi