答案1
在
ssh -t svf "cd ~/w/logs; tail -f some_file.log; exec $SHELL -l"
-t
表示本地 tty 线路规则设置为原始(直通)模式,并在远程端创建伪 tty。
该伪终端将获得默认设置,这在大多数系统上意味着例如一个^C
字符将导致 SIGINT 被发送到该伪终端的前台进程组。
在这里,当您ssh
使用命令时,sshd
会运行login-shell-of-the-remote-user -c that-command
.这是一个非交互式 shell,因此它不执行作业控制。
特别是,该 shell 像它运行的每个命令一样将在同一进程组中运行,该进程组是终端的前台进程组。
不过, shell $SHELL -l
(请注意,它$SHELL
在本地计算机上扩展,这可能不是您想要的)将是一个交互式 shell(因为它的调用没有,-c
并且它的 stdin 是终端设备),因此将在不同的进程组中运行命令(管道)并根据需要使它们成为或不成为终端的前台进程组。
在这里,为了使其仅tail -f
接收 SIGINT,或者至少仅杀死它而不是 shell,您有多个选项,具体取决于远程用户的登录 shell。
如果远程用户的登录 shell 是bash
或yash
,您可以执行以下操作:
ssh -t svf 'set -m; cd ~/w/logs && tail -f some_file.log; exec "$SHELL" -l'
该-m
选项启用作业控制。这告诉 shell 在单独的进程组中运行管道(如交互式 shell),并且(对于bash
和yash
,通常不适用于所有其他 shell)在前台运行的管道的进程组将成为终端设备的前台进程组(因此例如,它们得到SIGINT
on^C
或SIGTSTP
on ),而在后台启动的则不是(因此它们被禁止(接收 SIGTTIN)从终端读取)。^Z
这种方式tail -f
将在其自己的进程组中运行,该进程组将是前台进程组。如果在tail
运行时键入 CTRL+C ,则只会tail
收到 SIGINT。
如果您知道 shell 与 Bourne 类似,另一种选择是为 SIGINT 设置处理程序:
ssh -t svf 'trap : INT; cd ~/w/logs && tail -f some_file.log; exec "$SHELL" -l'
在这里,我们正在配置 shell,以便它:
在收到 SIGINT 时执行(无操作)命令。子进程将继承该处理程序,但在执行命令时,处理程序将重置为默认值(终止进程)。
所以现在,在运行时按 CTRL-C tail
,shell 和tail
都会收到 SIGINT,但只有 tail 会因此而死亡,并且 shell 将继续执行"$SHELL" -l
.
如果您知道bash
或yash
可以在远程主机上使用(并且远程 shell 类似于 Bourne 或 csh),您还可以执行以下操作:
ssh -t svf 'cd ~/w/logs && bash -c "set -m; tail -f some_file.log"; exec "$SHELL" -l'
我们bash
显式调用 shell 并打开该-m
选项,以便它将tail
在其自己的前台进程组中启动,并且只有它才会像第一个解决方案一样接收 SIGINT。
使用bash-4.3
或 以上,您可以将 替换bash -c "set -m;
为bash -mc "
。这不适用于旧版本,它会忽略传递给解释器的-m
(and -i
) 选项(bash
如果与-c
.
答案2
为了防止CTRL+C断开连接,它应该是交互式的。
这是通过 实现的bash -i
。所以我可以运行:
ssh -t svf 'cd ~/w/logs; bash -i tail -f some_file.log; exec $SHELL -l'
但这个原因那问题。要解决它,您可以创建解决方法。在远程计算机上创建脚本来运行命令(注意-i
标志。这很重要。如果您需要与正常登录时相同的 ENV,您-l
也可以添加标志):
$ cat ~/cmd
#!/bin/bash -i
cmd="$*"
eval $cmd
然后我将命令修改为:
ssh -t svf 'cd ~/w/logs; ~/cmd tail -f some_file.log; exec $SHELL -l'
现在我很高兴并且可以CTRL+C何时tail -f
运行。由于交互性,bash -i
ssh 连接不会中断,并且exec $SHELL -l
在调用热键后会执行命令。
重要的提示:您应该使用 ' 而不是 " 在远程端而不是本地扩展 $SHELL。感谢 Stéphane Chazelas