Bash: 如何让 telnet 或 nc 在后台工作直到连接关闭

Bash: how to make telnet or nc work in background until connection is closed

下面我将描述我正在挣扎的事情以及我目前做出的决定。 请指出 cleverer/smaller 的决定,也很乐意收到反馈

因此,本地主机上有一个发布者和一个客户端,它们通过端口 8080 进行通信。我可以 telnet 或 nc 到此端口并将输出正常写入日志,但无法使相同的命令在后台运行。

我看到的是,当在后台启动时,它们会在获得第一个输入后立即停止(真的如此吗?),但在前台,它们按应有的方式工作,只有在发布者关闭此端口的连接后才会死亡。

这是正常情况:

> telnet localhost 8080 | tee output.log (or >>output.log, no matter)
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

发布者开始通过端口发送信息。

***some necessary output***

发布者关闭端口。

Connection closed by foreign host.

但是在后台启动时它立即停止,不等待输出:

> nohup telnet localhost 8080 | tee output.log (or <command> &, or nohup <command> &)
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.

Here is an expect script that I`ve come to. It is being launched in background.

nohup ./telnet_expect.sh &

The script spawns new bash session and performs usual redirection to a file.

Usually I arrange communicating between bash and expect via env variables, here I have not implemented it because the whole use case is small enough.

#!/bin/bash

echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Started telnet_expect.sh\n"

logFile="/export/home/<user>/<folder>/output.log"
logFileExpect="/export/home/<user>/<folder>/expect.log"

echo > "$logFile"
echo > "$logFileExpect"

until nc -w 1 localhost 8080
do
    sleep 1
done

expect -c "
log_user 1
set timeout 250

proc err_exit {msg} {
        puts \"---\"
    puts stderr \"$msg\"
        send \"exit status: $?\r\"
    exit 1
}

exp_internal -f /export/home/<user>/<folder>/expect.log 1

spawn bash

send \"telnet localhost 8080 >> /export/home/<user>/<folder>/output.log\r\"
sleep 10
expect {
    \"*onnection closed by foreign host*\" {
        send \"echo Success\r\"
    }
    timeout {
        err_exit \"\nError: timeout\n\"
    }
}

send \"exit\r\"

expect eof
"

echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Finished telnet_expect.sh\n"

Upd: below is more configurable version which takes log file as an argument. This is how to launch:

nohup ./telnet_expect.sh <your_output_log_file> &

The values of IP and port here are defined only once, so it is easy to move them to arguments as well. Also there are debug log and console log to understand what exactly is going on. User can provide either absolute or relative path to a log file.

#!/bin/bash

scriptFolderPath=$(dirname $(readlink -f "[=13=]"))
logDir="$scriptFolderPath"
logFileDefault="$logDir"/"output_default.log"
logFileExpect="$logDir"/"expect.log"
ip="localhost"
port="8080"

logConsole="$logDir"/"console_telnet.log"
echo > "$logConsole"

echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Started telnet_expect.sh\n" >> "$logConsole"

### debug info
logDebug="$logDir"/"debug_telnet.log"
echo > "$logDebug"
exec 5> "$logDebug"
BASH_XTRACEFD="5"
PS4='$LINENO: '
set -x

if [ -z "" ]; then
    logFile="$logFileDefault"
else
    if [[ $logFile =~ "/" ]]; then
        logFile=""
    else
        echo "Got the log file in the same folder as the script" >> "$logConsole"
        logFile="$scriptFolderPath"/""
    fi
fi

echo > "$logFile"
echo > "$logFileExpect"

### setting envs
echo -e "\nsetting BCKGR_TELNET_LOG" >> "$logConsole"
export BCKGR_TELNET_LOG="$logFile"
echo "set BCKGR_TELNET_LOG: $BCKGR_TELNET_LOG" >> "$logConsole"

echo -e "\nsetting BCKGR_TELNET_LOG_EXPECT" >> "$logConsole"
export BCKGR_TELNET_LOG_EXPECT="$logFileExpect"
echo -e "set BCKGR_TELNET_LOG_EXPECT: $BCKGR_TELNET_LOG_EXPECT\n" >> "$logConsole"

echo -e "\nsetting BCKGR_TELNET_LOG_PORT" >> "$logConsole"
export BCKGR_TELNET_LOG_PORT="$port"
echo -e "set BCKGR_TELNET_LOG_PORT: $BCKGR_TELNET_LOG_PORT\n" >> "$logConsole"

echo -e "\nsetting BCKGR_TELNET_LOG_IP" >> "$logConsole"
export BCKGR_TELNET_LOG_IP="$ip"
echo -e "set BCKGR_TELNET_LOG_IP: $BCKGR_TELNET_LOG_IP\n" >> "$logConsole"

until nc -w 1 "$ip" "$port"
do
    sleep 1
done

expect -c "
log_user 1
set timeout 250

proc err_exit {msg} {
        puts \"---\"
    puts stderr \"$msg\"
        send \"exit status: $?\r\"
    exit 1
}

puts \"Reading BCKGR_TELNET_LOG_EXPECT\"

if {[info exists env(BCKGR_TELNET_LOG_EXPECT)]} {
    set bckgr_telnet_log_expect $::env(BCKGR_TELNET_LOG_EXPECT)
        puts \"Found BCKGR_TELNET_LOG_EXPECT\"
} else {
        err_exit \"Error while reading env variable BCKGR_TELNET_LOG_EXPECT\"
}

puts \"Reading BCKGR_TELNET_LOG\"

if {[info exists env(BCKGR_TELNET_LOG)]} {
    set bckgr_telnet_log $::env(BCKGR_TELNET_LOG)
        puts \"Found BCKGR_TELNET_LOG\"
} else {
        err_exit \"Error while reading env variable BCKGR_TELNET_LOG\"
}

puts \"Reading BCKGR_TELNET_LOG_PORT\"

if {[info exists env(BCKGR_TELNET_LOG_PORT)]} {
    set bckgr_telnet_log_port $::env(BCKGR_TELNET_LOG_PORT)
        puts \"Found BCKGR_TELNET_LOG_PORT\"
} else {
        err_exit \"Error while reading env variable BCKGR_TELNET_LOG_PORT\"
}

puts \"Reading BCKGR_TELNET_LOG_IP\"

if {[info exists env(BCKGR_TELNET_LOG_IP)]} {
    set bckgr_telnet_log_ip $::env(BCKGR_TELNET_LOG_IP)
        puts \"Found BCKGR_TELNET_LOG_IP\"
} else {
        err_exit \"Error while reading env variable BCKGR_TELNET_LOG_IP\"
}

exp_internal -f $bckgr_telnet_log_expect 1

spawn bash

send \"telnet $bckgr_telnet_log_ip $bckgr_telnet_log_port >> $bckgr_telnet_log\r\"
sleep 10
expect {
    \"*onnection closed by foreign host*\" {
        send \"echo Success\r\"
    }
    timeout {
        err_exit \"\nError: timeout\n\"
    }
}

send \"exit\r\"

expect eof
"

echo -e "\nUnsetting env variables" >> "$logConsole"
unset BCKGR_TELNET_LOG
unset BCKGR_TELNET_LOG_EXPECT
unset BCKGR_TELNET_LOG_PORT
unset BCKGR_TELNET_LOG_IP

echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Finished telnet_expect.sh\n" >> "$logConsole"

您改用 netcat 命令:

nohup nc -d localhost 8080 >>console-8080.txt &

如果您想缩进带有日期的日志文件:

nohup nc -d localhost 8080 | while read line ; do echo `date +'%d%m%Y-%H%M%S'`: $line >>console-8080.txt; done &

重启 nc 进程以防它关闭:

#!/bin/bash
ip=
port=
while [ 1 ]
do
    nc -d $ip $port | while read line ; do echo `date +'%d%m%Y-%H%M%S'`: $line >>console-$ip-$port.txt; done
    sleep .1
done