linux - Linux bash 递归函数的返回值

标签 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

最佳答案

我无法想象在很多情况下,我会经常使用循环中延迟一秒的代码,以至于值得将其编写为函数 - 我会使用相对直接(迭代)的脚本。但是,如果您确定将脚本转换为函数对您有利,那么将脚本转换为函数也并非不可能。你的情况和我的不一样。

#!/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 未执行时才会到达结束 block ,因此 ping 失败并且服务器“关闭”(或不存在)。然后,您会收到一 strip 时间戳的消息,表明服务器已关闭,并以非零状态退出以指示失败。

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

也可以将其编写为一个简单的计数循环,用于测试 ping 的状态,成功时退出,并进行报告和重试。然而,当循环退出而没有双重测试 $Trial 的值时,避免最后一次 sleep 1 是很棘手的。这在运行时并不昂贵,但它是重复的根源,而 DRY(不要重复自己)是一个值得遵守的原则。

#!/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

我并不完全热衷于此,但它可以与 Bash 和 Korn shell 配合使用。

将最后一个脚本转换为函数基本上很简单 - 将 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中,我读取了该函数:

$ . upip-func.sh
$ upip www.google.com
server: www.google.com, trial 1
$ echo $?
0
$ upip ping.google.com
server: ping.google.com, trial 1
bash: ping.google.com is down: checking again after 1 sec
server: ping.google.com, trial 2
2016-06-06 00:35:18: Server ping.google.com is DOWN
$ echo $?
1
$ if upip www.google.com; then echo OK; else echo Fail; fi
server: www.google.com, trial 1
OK
$ if upip ping.google.com; then echo OK; else echo Fail; fi
server: ping.google.com, trial 1
bash: ping.google.com is down: checking again after 1 sec
server: ping.google.com, trial 2
2016-06-06 00:38:32: Server ping.google.com is DOWN
Fail
$

关于linux - Linux bash 递归函数的返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37630609/

相关文章:

c++ - 在使用密码执行 C++ 二进制文件时切换到 Linux root 帐户,可能吗?

linux - Hook 系统调用ubuntu

c - 如果正在等待的线程自行分离怎么办?

regex - grep 多行模式

java - 如何创建一个采用 List 类型且输出是不同 List 的递归方法?

algorithm - 找出每个节点的大小

c - 使用 sigaction() 实现 siginterrupt()?

linux - 将文件递归重命名为 ASCII 标准

bash - Docker无法识别用作参数的bash变量

algorithm - 了解运行时间的递归