停止发送到后台的 ssh 进程,但不破坏标准输出

停止发送到后台的 ssh 进程,但不破坏标准输出

我的脚本旨在使用提取文本日志文件tail -f并使用提取 wireshark 跟踪tshark。但我不知道这些是否是实现我的目标的最佳选择。

我的脚本必须ssh进入一台机器(我称之为服务器),然后从那台机器ssh进入另一台机器(称为刀片),所以我创建了这两个函数来简化发送命令:

processIDs=()

# sends command $2 to server $1, piping output to file $3 on local machine
server_cmd() {
    ssh -i /home/$USER/.ssh/id_rsa root@$1 $2 1>>$3 2>>$errorOutput &
    processIDs+=($!)
}

# sends command $3 to blade $2 of server $1, piping output to file $4 on local machine
blade_cmd() {
    server_cmd $1 "ssh root@$2 \"$3\"" $4
}

ssh每次我向后台发送调用时,进程 ID 都会存储到数组中。

在我的脚本中,我对该函数进行了不同次数的调用(取决于用户的选择)blade_cmd

blade_cmd $server_ip $server_blade_ip "tail -f \\\$(ls -1tr ${path}_Debug_* | tail -1)" debug.log
blade_cmd $server_ip $server_blade_ip "tail -f \\\$(ls -1tr ${path}_Report_* | tail -1)" report.log

blade_cmd $server_ip $server_blade_ip "tshark -i eth7 -w -" tshark.pcap

然后执行生成日志/跟踪的操作,然后像这样终止进程:

# kill all generated processes on the array
for i in ${!processIDs[@]}; do
    kill ${processIDs[i]}
    wait ${processIDs[i]} 2>>$errorOutput
done

但是通过这种设置,远程机器上的进程就不会被终止,而是处于挂起状态。

我发现终止进程的解决方案是使用ssh标志-tt强制调用tty,这确实解决了不传播来自本地机器的终止的问题,但随后我收到的日志/跟踪会被登录横幅和各种换行符破坏,这会使日志,特别是跟踪变得tshark毫无用处。

我需要一些关于如何继续进行此事的指导。

答案1

tshark -w - …在我的测试中,当本地退出时,远程会自动退出ssh,即使我ssh像你一样链接 s;至少在tshark尝试继续写入时。我认为这是此处描述的机制:tail -f … | grep -q …为什么找到匹配项后不退出?

注意本地ssh退出后的第一次写入可能只会导致中间ssh(在服务器)退出,下一次写入将导致tshark退出。

类似于tail -f刀刃,但这tail可能(或可能不)“智能”并能检测到破裂的管道(请参阅链接的答案)。但中间体还ssh没有那么智能。

所以是的,在某些情况下“远程机器上的进程[…]处于悬而未决的状态”。

有一个技巧可以让远程tsharktail -f退出在本地ssh退出后立即退出。您不能将此技巧用于应从 stdin 读取的远程命令(即最终从本地的 stdin 读取ssh),但由于tsharktail -f不使用它们的 stdin,在这种情况下这个技巧很有用。这是技巧:

刀刃,而不是tshark …运行:

# shell code for blade
tshark … & cat >/dev/null; kill "$!"

注意,您需要在本地调整引用,以便$!扩展刀刃,而不是更早。为了简洁起见,我决定以 shell 的形式发布命令刀刃应得。

tshark …现在在后台运行刀刃,但这不应该阻止它工作。cat最终从本地的标准输入读取ssh,它将一直停留在那里,直到本地ssh退出或其标准输入中断或耗尽。如果本地退出ssh或其标准输入中断或耗尽,cat将获得 EOF 条件并退出。然后killtshark终止刀刃

tshark和不再存在catkill,壳刀刃sshd退出和相关实例刀刃退出,ssh等等服务器也退出了。很干净。

可以使用相同的技巧来tail -f …退出。

现在我们需要照顾当地的情况。

远程cat将读取本地ssh读取的内容。如果没有重定向,本地ssh将使用本地脚本的标准输入(标准输入可能是终端、常规文件或其他任何东西)。通常,您可能不想让本地ssh使用脚本的标准输入。此外,如果 EOF 条件过早发生,cat则会过早退出;我们希望它仅在本地ssh退出后退出,而不是在本地标准输入耗尽时退出。出于这些原因,最好将本地的标准输入连接ssh到脚本的标准输入以外的其他东西。这不可能是/dev/null因为 EOF 会立即发生。这不应该是/dev/zero因为它会白白传输零字节。这可能是tail -f /dev/null

# locally inside your function
tail -f /dev/null | ssh … &

但随后$!会告诉您 PID,ssh并且在终止ssh此本地进程后tail会保留,除非它是“智能的”(再次参见链接的答案)。只有当您的本地进程tail是“智能的”时,这才是一种干净的方法,不会留下挂起的进程。

或者,您可以使用虚拟 fifo。您应该事先将其打开以进行读写,这样就不会出现任何停滞。不过,无需通过 fifo 传递任何内容。示例:

# locally
mkfifo dummy
exec 3<>dummy
rm dummy

# then inside your function
<&3 ssh … &

您可以将同一个 fifo 用于多个 实例ssh。在示例中,我在打开文件描述符后立即将 fifo 从目录中取消链接,因此以后无需维护。当 fifo 不再使用时,内核将真正摆脱它。

不管怎样(比如用“智能”tail或先进先出),现在只要ssh在本地杀死,然后远程cat打开刀刃会看到 EOF 并退出,刀刃服务器将如上所述发生。

相关内容