ssh + 此处文档 – Ctrl+C 能到达远端吗?

ssh + 此处文档 – Ctrl+C 能到达远端吗?

作为我必须执行的一系列任务的一部分,我必须构建一个 bash 脚本来访问远程计算机,执行 3 个命令,等待进程通过SIGINT(或简单结束)结束,然后再进行一些清理。

目前,我正在使用这个代码:

#! /bin/bash

# local preparations
# ...

ssh -t [email protected] <<-'COMMANDS'

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID" INT HUP
    wait $executePID

    #cleanup code here

    echo "done. Logging out"
    sleep 2
    logout
COMMANDS

# Final local cleanup

该代码似乎只起作用,直到找到wait(内置)命令。wait似乎正在消耗它之后的所有命令,因此,当我尝试发送SIGINTCtrl+ C)时,它似乎无法执行陷阱内容和所有清理代码。

我该如何修复这个问题以便它能按照我期望的方式工作?

我不能将此 bash 文件拆分成多个文件,也不能在远程计算机上创建任何脚本,即使是临时脚本。
两台计算机都运行相同版本的 Linux。

答案1

解释

这不是关于wait。我认为情况是这样的:

你使用<<,所以stdinssh重定向到某个描述符,通过该描述符整个这里的文件流。

其他答案解释了如何ssh能够捕获Ctrl+C何时-t使用。这才是最重要的:

在客户端,ssh将尝试将tty所使用的设置stdin为“原始”模式[…]设置原始模式意味着通常会发送信号的字符(例如Ctrl+ C)只会插入到输入流中。

在您的情况下,它与您的本地 shell 使用的不一样stdintty您的本地 shell 使用的保持不变,它永远不会设置为“原始”模式。

因此,当您按下Ctrl+时C,它会在本地执行并终止ssh。此时,远程端会获得SIGHUP。您的trap工作并终止java。我认为这里有一个陷阱: atrap响应给定信号执行一些代码,但它不会阻止信号发挥其正常作用。因此在我看来,java即使没有 a ,你的也会被杀死,trap因为它是响应 而终止的 shell 的工作SIGHUP

被终止的 shell 停止读取和解释其stdin。这就是为什么后面的所有内容wait都会被丢弃。


解决方案

commands() { cat <<-'COMMANDS'

    cleanup() {
    # cleanup code here
    echo "Done. Logging out"
    sleep 2
    logout
    }

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID; cleanup" INT HUP
    wait $executePID

    cleanup
COMMANDS

}

stty raw -echo; cat <(commands) - | ssh -t [email protected]; stty -raw echo

最后一行是实际命令。首先,我们准备tty+Ctrl无法C在本地执行。然后,我们将命令和标准输入连接起来,并将其传递给远程 shell。我无法使用这里的文件直接地,该command函数是一种解决方法(使用常规文件会更容易,但您说您不能使用多个文件)。退出后,ssh我们cat将设置tty为正常状态。

拥有伪终端是至关重要的,因此确保ssh -t它可以工作(-tt如果需要就使用)。

远程 shell 必须读取(缓冲)来自 的所有命令commands,然后才能在您按下它时获取Ctrl+ C。我猜这意味着 之后不能有太多代码wait。此外,无论你想在 之后做什么,SIGINT都必须从 内部运行trap。这就是我使用一个cleanup函数来完成所有操作的原因。

该代码并非万无一失。如果过早或多次按下Ctrl+ ,您就会发现自己处于远程 shell 中,或者本地 有点“损坏” 。在后一种情况下,请使用命令重置。Cttyresettty

Enter看到“与…的连接已关闭”后,您必须按一个键(例如)。原因是直到尝试向管道写入某些内容时cat,它才会注意到管道已损坏(因为ssh不再存在)。


选择

如果出于某种原因上述解决方案不适合您,请使用这个更简单的替代方案。这里的文件与之前完全一样。在这种情况下,我们不会干扰ttyCtrl+C终止本地,ssh就像它对您的原始代码所做的那样。区别(就您的代码而言)在于trap进行清理。我认为SIGHUP仅捕获就足够了。

ssh -t [email protected] <<-'COMMANDS'

    cleanup() {
    # cleanup code here
    echo "Done. Logging out"
    sleep 2
    logout
    }

    echo "Preparing execution"

    java -jar execute.jar &
    executePID=$!

    echo "Ready."
    echo "CTRL+C to clean and close"

    trap "kill $executePID; cleanup" INT HUP
    wait $executePID

    cleanup
COMMANDS

注意:trap触发时cleanup会回显一条消息,但您看不到它,因为您的本地ssh已断开连接。只有在java没有退出时,您才会看到该消息trap。您的清理代码(# cleanup code here)仍应执行。

相关内容