作为我必须执行的一系列任务的一部分,我必须构建一个 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
似乎正在消耗它之后的所有命令,因此,当我尝试发送SIGINT
(Ctrl+ C)时,它似乎无法执行陷阱内容和所有清理代码。
我该如何修复这个问题以便它能按照我期望的方式工作?
我不能将此 bash 文件拆分成多个文件,也不能在远程计算机上创建任何脚本,即使是临时脚本。
两台计算机都运行相同版本的 Linux。
答案1
解释
这不是关于wait
。我认为情况是这样的:
你使用<<
,所以stdin
被ssh
重定向到某个描述符,通过该描述符整个这里的文件流。
其他答案解释了如何ssh
能够捕获Ctrl+C何时-t
使用。这才是最重要的:
在客户端,
ssh
将尝试将tty
所使用的设置stdin
为“原始”模式[…]设置原始模式意味着通常会发送信号的字符(例如Ctrl+ C)只会插入到输入流中。
在您的情况下,它与您的本地 shell 使用的不一样stdin
。tty
您的本地 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 中,或者本地 有点“损坏” 。在后一种情况下,请使用命令重置。Ctty
reset
tty
Enter看到“与…的连接已关闭”后,您必须按一个键(例如)。原因是直到尝试向管道写入某些内容时cat
,它才会注意到管道已损坏(因为ssh
不再存在)。
选择
如果出于某种原因上述解决方案不适合您,请使用这个更简单的替代方案。这里的文件与之前完全一样。在这种情况下,我们不会干扰tty
,Ctrl+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
)仍应执行。