Linux脚本使用Netcat在x小时后停止工作

10

我有两个脚本:

#!/bin/bash

netcat -lk -p 12345 | while read line
do
    match=$(echo $line | grep -c 'Keep-Alive')
    if [ $match -eq 1 ]; then
        [start a command]
    fi
done
并且
#!/bin/bash

netcat -lk -p 12346 | while read line
do
    match=$(echo $line | grep -c 'Keep-Alive')
    if [ $match -eq 1 ]; then
        [start a command]
    fi
done

我把这两个脚本放在了“/etc/init.d/”

当我重启我的Linux机器(RasbPi)时,这两个脚本都能够正常工作。

我试过大约20次,它们一直能够正常工作。

但是大约12个小时后,整个系统就停止工作了。我加入了一些日志记录,但似乎这些脚本不再响应了。但当我;

ps aux

我能看到脚本仍在运行:

root      1686  0.0  0.2   2740  1184 ?        S    Aug12   0:00 /bin/bash /etc/init.d/script1.sh start
root      1689  0.0  0.1   2268   512 ?        S    Aug12   0:00 netcat -lk 12345
root      1690  0.0  0.1   2744   784 ?        S    Aug12   0:00 /bin/bash /etc/init.d/script1.sh start
root      1691  0.0  0.2   2740  1184 ?        S    Aug12   0:00 /bin/bash /etc/init.d/script2.sh start
root      1694  0.0  0.1   2268   512 ?        S    Aug12   0:00 netcat -lk 12346
root      1695  0.0  0.1   2744   784 ?        S    Aug12   0:00 /bin/bash /etc/init.d/script2.sh start

重启后它们又开始工作了...但这是罪恶的,定期重新启动Linux机器...

我插入了一些日志记录,结果如下;

Listening on [0.0.0.0] (family 0, port 12345)
[2013-08-14 11:55:00] Starting loop.
[2013-08-14 11:55:00] Starting netcat.
netcat: Address already in use
[2013-08-14 11:55:00] Netcat has stopped or crashed.
[2013-08-14 11:49:52] Starting loop.
[2013-08-14 11:49:52] Starting netcat.
Listening on [0.0.0.0] (family 0, port 12345)
Connection from [16.8.94.19] port 12345 [tcp/*] accepted (family 2, sport 6333)
Connection closed, listening again.
Connection from [16.8.94.19] port 12345 [tcp/*] accepted (family 2, sport 6334)
[2013-08-14 12:40:02] Starting loop.
[2013-08-14 12:40:02] Starting netcat.
netcat: Address already in use
[2013-08-14 12:40:02] Netcat has stopped or crashed.
[2013-08-14 12:17:16] Starting loop.
[2013-08-14 12:17:16] Starting netcat.
Listening on [0.0.0.0] (family 0, port 12345)
Connection from [16.8.94.19] port 12345 [tcp/*] accepted (family 2, sport 6387)
Connection closed, listening again.
Connection from [16.8.94.19] port 12345 [tcp/*] accepted (family 2, sport 6388)
[2013-08-14 13:10:08] Starting loop.
[2013-08-14 13:10:08] Starting netcat.
netcat: Address already in use
[2013-08-14 13:10:08] Netcat has stopped or crashed.
[2013-08-14 12:17:16] Starting loop.
[2013-08-14 12:17:16] Starting netcat.
Listening on [0.0.0.0] (family 0, port 12345)
Connection from [16.8.94.19] port 12345 [tcp/*] accepted (family 2, sport 6167)
Connection closed, listening again.
Connection from [16.8.94.19] port 12345 [tcp/*] accepted (family 2, sport 6168)

谢谢


我刚刚尝试过了,但是那样会让一切停止工作... - Dennis
3
“但那是个罪过。” 我怀疑,尤其是考虑到netcat上的“-k”保持活动标志,在许多小时后IP层是否会反弹,可能通过DHCP租约到期或您的以太交换机的“自我修复(即每天重新启动,因为它比修复错误更容易)”功能。 是否/var/log/syslog给你任何线索? - msw
关于DHCP租约时间的观点很好...我要测试一下...我的租约时间是24小时。有什么适当的解决方案吗?(我已经检查了/var/log/syslog,但是找不到任何有用的信息。但是再说一遍,我不太确定我应该注意什么...(我对Linux不太擅长) - Dennis
1
我想知道将其放在一个循环中,在重新启动netcat之前休眠约4秒钟,是否是一个好的解决方法。但当然,你仍然需要知道它的真正原因。可能它并不真正与netcat有关,而是与接口本身或外部连接有关。 - konsolebox
我认为从netcat会话中看到输出是很有趣的。尝试添加-v标志并将错误输出导入文件。应该像这样:netcat -vlk 12345 2>>/var/netcaterr.out | while read line ...在它停止工作后,查看/var/netcaterr.out并看看你能找到什么。 - Bex
显示剩余7条评论
6个回答

6
如果你的命令中包括 netcat 并且没有从标准输入(stdin)读取输入,则可以使其完全独立于终端运行。有时候后台进程仍依赖于终端,当它们在后台尝试从终端读取输入时会暂停(S)。实际上,由于你正在运行一个守护进程,应确保你的所有命令都不从终端(stdin)读取输入。
#!/bin/bash

set +o monitor # Make sure job control is disabled.

(
    : # Make sure the shell runs a subshell.
    exec netcat -lk -p 12345 | while read line  ## Use exec to overwrite the subshell.
    do
        match=$(echo $line | grep -c 'Keep-Alive')
        if [ $match -eq 1 ]; then
            [start a command]
        fi
    done
) <&- >&- 2>&- </dev/null &>/dev/null &

TASKPID=$!
sleep 1s ## Let the task initialize a bit before we disown it.
disown "$TASKPID"

我认为我们可以再次尝试记录日志:

set +o monitor

(
    echo "[$(date "+%F %T")] Starting loop with PID $BASHPID."

    for (( ;; ))
    do
        echo "[$(date "+%F %T")] Starting netcat."

        netcat -vv -lk -p 12345 | while read line
        do
            match=$(echo "$line" | grep -c 'Keep-Alive')
            if [ "$match" -eq 1 ]; then
                [start a command]
            fi
        done

        echo "[$(date "+%F %T")] Netcat has stopped or crashed."

        sleep 4s
    done
) <&- >&- 2>&- </dev/null >> "/var/log/something.log" 2>&1 &

TASKPID=$!
sleep 1s
disown "$TASKPID"

那我应该尝试第二个,带有日志记录,但是这不也应该包括'exec'在子shell中运行吗? - Dennis
老实说,我不确定记录日志是否会再次导致脚本停止,也许我可以建议您先尝试带有日志记录的脚本,然后再尝试不带日志记录的脚本。关于 exec 命令,不用担心,因为使用了(),它已经从其父 shell 中分离出来,并且希望也与终端的属性分离开来。如果仍然不起作用,我建议使用其他的 netcat,例如原始的 netcat 或 gnu-netcat。 - konsolebox
结果;大约一个小时后停止工作;日志的最后一行:连接来自x.x.x.x端口12345 [tcp/*]已接受(family2, sport 6386)。 - Dennis
它没有显示“Netcat已停止或崩溃”,是吗? - konsolebox
你还应该检查与netcat连接的子shell(| while read ...)是否不会进入无限等待或循环,或死锁。你也可以在其中添加调试消息,使用echo "[$(date "+%F %T")] <message>." >&2来了解命令块的最后一部分或崩溃前的最后位置。如果确实是netcat,则可以从二进制包或源安装新的或更稳定的版本。 - konsolebox
显示剩余2条评论

5
关于循环,它可能看起来像这样。

#!/bin/bash

for (( ;; ))
do
    netcat -lk -p 12345 | while read line
    do
        match=$(echo "$line" | grep -c 'Keep-Alive')
        if [ "$match" -eq 1 ]; then
            [start a command]
        fi
    done
    sleep 4s
done

为了更加安全,可以添加双引号。

您可以尝试使用以下格式捕获错误并添加一些日志记录:

#!/bin/bash

{
    echo "[$(date "+%F %T")] Starting loop."

    for (( ;; ))
    do
        echo "[$(date "+%F %T")] Starting netcat."

        netcat -lk -p 12345 | while read line
        do
            match=$(echo "$line" | grep -c 'Keep-Alive')
            if [ "$match" -eq 1 ]; then
                [start a command]
            fi
        done

        echo "[$(date "+%F %T")] Netcat has stopped or crashed."

        sleep 4s
    done
} >> "/var/log/something.log" 2>&1

如果您按照以下格式编写读取命令,则可以更好地读取未修改的行:

... | while IFS= read -r line

有些人可能会建议使用进程替换,但这次我不建议这样做,因为通过“| while ...”方法,while循环将能够在子shell上运行,并保持外层的for循环安全,以防它崩溃。此外,while循环中并没有真正需要在外部使用的变量。
实际上,我现在有一个想法,问题可能实际上与输入及其如何处理“while read line; do ...; done”块有关,而不是与netcat本身有关。您的变量没有正确引用""可能是其中之一,也可能是导致您的netcat崩溃的实际原因。

好东西!加了一些日志记录,还加了一个额外的循环以防止netcat停止...我现在要试一下,如果这个有效,我会给你积分的!谢谢! - Dennis
如果您没有以root身份运行它,并且如果您的用户在写目录上没有写入权限,也许您可以只使用主目录:} >> ~/something.log 2>&1,或者以root身份创建具有用户写入权限的文件:touch /var/log/something.log; chown youruser:yourusersgroup /var/log/something.log; chmod 644 /var/log/something.log # 或者使用您偏好的600 - konsolebox
我正在以root身份运行,所以一切都很好。当事件被触发时,我还添加了一行日志代码。我真的认为我们在正确的方向上。现在我们可以确定它在哪个阶段崩溃了。我还发现,在启动期间脚本会出现错误3次,并重新启动3次。感谢您帮助我解决语法问题,如果是PowerShell,对我来说就没有问题了,但我在Linux方面不是那么擅长... - Dennis
我记得。你还可以使用netcat的-v选项添加更多消息。它只将详细信息发送到stderr(fd 2),而不是管道,因此它不会影响进程。 netcat -vv -lk -p 12345 | while IFS= read -r line - konsolebox
root 1686 0.0 0.2 2740 1184 ? S Aug12 0:00 /bin/bash /etc/init.d/script1.sh start root 1689 0.0 0.1 2268 512 ? S Aug12 0:00 netcat -lk 12345 root 1690 0.0 0.1 2744 784 ? S Aug12 0:00 /bin/bash /etc/init.d/script1.sh start root 1691 0.0 0.2 2740 1184 ? S Aug12 0:00 /bin/bash /etc/init.d/script2.sh start root 1694 0.0 0.1 2268 512 ? S Aug12 0:00 netcat -lk 12346 root 1695 0.0 0.1 2744 784 ? S Aug12 0:00 /bin/bash /etc/init.d/script2.sh start - Dennis
script2没有一个启动函数,这就是原因。 - Dru

3
你提到了"大约12小时后,整个系统就停止工作了" - 很可能是脚本执行了你在[启动命令]中的内容并导致了内存膨胀。你确定[启动命令]没有频繁地分叉出很多进程并释放内存吗?

好的,为了排除这个问题,我需要删除该命令并将其输出到日志文件中。以查看是否在没有我的启动脚本的情况下继续工作。 - Dennis
所以您的意思是已经删除了 [开始命令] 部分,但是您的脚本仍然在12小时后没有响应? - SSaikia_JtheRocker
是的,我也尝试过使用日志记录。在启动命令之前记录日志条目,然后在命令返回时记录另一个日志条目。 - Dennis
我说的是完全删除你在 [开始一个命令] 中使用的命令,这样你就会知道使用该命令是否会使系统变得臃肿。 - SSaikia_JtheRocker

3

我经常遇到ncnetcat的奇怪行为。你应该看看ncat,它几乎是相同的工具,但在所有平台上表现相同(ncnetcat的行为取决于发行版、Linux、BSD、Mac)。


我自己也遇到了一些奇怪的行为。感谢你提供替代方案的建议。 - Ron Burk

2

定期地,netcat会打印出一块二进制数据,而不是一行文本。这很可能导致read命令失败。

我认为您正在使用此程序验证远程主机是否仍连接到端口12345和12346,并且未被重新启动。

我的解决方案是将netcat的输出导入sed,然后将(大大减少的)行传递给read命令...

#!/bin/bash

{
    echo "[$(date "+%F %T")] Starting loop."

    for (( ;; ))
    do
        echo "[$(date "+%F %T")] Starting netcat."

        netcat -lk -p 12345 | sed 's/.*Keep-Alive.*/Keep-Alive/g' | \
        \
        while read line
        do
            match=$(echo "$line" | grep -c 'Keep-Alive')
            if [ "$match" -eq 1 ]; then
                [start a command]
            fi
        done

        echo "[$(date "+%F %T")] Netcat has stopped or crashed."

        sleep 4s
    done
} >> "/var/log/something.log" 2>&1

此外,您需要审查/etc/init.d中的其他一些启动程序,以确保它们与系统使用的rc版本兼容,尽管从init.d中的某个简单文件的副本中调用script2.sh会更容易。目前,script2是启动脚本,但不符合您使用的init包要求。
听起来比我想象的更复杂...让我更好地解释一下:
/etc/init.d/syslogd        ## a standard init script that calls syslogd
/etc/init.d/start-monitor   ## a copy of a standard init script that calls script2.sh

作为额外的说明,我认为你可以将netcat绑定到你正在监视的特定IP上,而不是将其绑定到所有地址0.0.0.0。

ksh有一个read -r(原始读取),也许还有一个-b二进制选项。不确定bash是否有。祝大家好运。 - shellter

1

如果您正在等待传入的连接请求,则不应使用-p选项。(请参阅nc的man页面) 主机名和端口是命令行的最后两个参数。

可能会连接到自己的端口,几个小时后会出现某些资源缺失?


好的观点,但我也注意到阅读netcat的man页面时。我已经删除了-p选项。但这并没有帮助。你在第二条评论中的意思是什么? - Dennis
这只是一种隐约的怀疑。我从未以这种方式使用过netcat。-p选项是用于发出连接请求的选项。模糊的怀疑是:由于该参数设置,netcat可能会尝试发起某些请求。但你说这没有区别。 - tue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接