我在 Ubuntu 16.04 中有一个名为 的 shell 程序,foo
当我按下任何按钮时,它会输出状态更新。它位于/usr/local/bin/foo
,所以我可以在任何地方调用该程序。该程序的工作原理如下:
$ foo
Welcome
当我按下一个键后,它会显示:
Time now is 01:23:45
如果我按Ctrl-C
,它将像大多数其他 shell 程序一样退出。
我希望这个foo
程序在多个实例中运行。我知道 GNU Screen 命令可以在任务之间切换,但这是正确的方法吗?
或者,我知道我可以让程序在后台运行,如下所示:
$ foo &
[1] 123456
作业 ID 1 是在后台创建的,我可以通过它的作业 ID 来调用它:
$ fg %1
但是,在获得程序的最新输出后,我需要将其放回后台(通过按任意按钮:是否有任何方法可以以编程方式执行此操作?)。我怎样才能实现这个目标?
答案1
据我所知,您的要求是能够:
- 同时运行该程序的多个实例。
- 随时获得对任何实例的终端控制。
- 实例输出后立即将其发送回后台。
如果我们坚持这种&
方法,那么是的,(3)有一个捷径。当程序控制终端时,可以使用信号将其发送到后台SIGTSTP
。很方便,这通常就是Ctrl+ 的Z作用。
因此你可以有这样的东西:
$ foo &
Welcome
[1] 10
$ foo &
Welcome
[2] 11
$ fg %1
<ENTER>
Time now is 01:23:45
<Ctrl+Z>
[1]+ Stopped
据我所知,没有这样的捷径fg
(以及您如何指示要带回哪个实例?)
请注意,SIGTSTP
信号实际上不需要来自启动进程的 shell。如果您kill -TSTP
在第二个 shell 中通过 PID 进行处理,您也可以fg
在第一个 shell 中将其恢复。
在获得程序的最新输出后,我需要将其放回后台(通过按任意按钮:是否有任何方法可以以编程方式执行此操作?)
如果通过以编程方式您的意思是您可以访问foo
的源代码,然后您可以使用我们刚刚看到的内容。程序raise
可以使用系统调用向自身发送信号。如果我们采用一个非常简单的版本foo
,我们可以得到这样的结果:
#include <stdio.h>
#include <signal.h>
int main(void)
{
printf("Welcome");
while(getchar()) { /* Use termios to switch to unbuffered mode */
fprintf(stdout, "The time is... something");
fflush(stdout);
fflush(stdin);
raise(SIGTSTP);
}
return 0;
}
虽然这一切都相当不错,但我同意这screen
可能是完成这一切的更好方法,主要是因为您可以命名屏幕会话,因此永远不需要记住作业或进程 ID。就像该&
方法一样,它也具有不需要更改源代码的优点。
$ screen -S foo1
$ ./foo
Welcome
<Ctrl-a d>
[detached from 10.foo1]
$ screen -s foo1
<ENTER>
Time now is 01:23:45
<Ctrl-a d>
[detached from 10.foo1]
这里的另一个优点是您不需要在分离之前停止程序。如果您希望它在您不注意时在后台执行操作,这非常方便。在之前的方法中,bg
每次停止时都需要运行foo
。
您还可以使用 获取正在运行的屏幕列表screen -ls
。在我的机器上,我设置了一些别名~/.bashrc
来解决我额外的懒惰:
alias S='screen -S'
alias s='screen -r'
alias sls='screen -ls'
S foo1
通过这种方式,您可以简单地执行诸如, , ...之类的操作。s foo1
有些人可能会争辩说,当您在命令中放错空格或其他内容时,单字符别名可能会有点烦人,但我认为screen
这是其中一个程序太方便了您有能力破例。
现在假设您采用该screen
方法,以下是如何将密钥发送到会话并从 shell 获取其最后一行输出。有一些方法可以调整这个(我已经允许自己走一些捷径)但无论如何。您可以在中定义以下函数~/.bashrc
:
screen_key_tail() {
screen -S $1 -X stuff "^M"
tmp="$(mktemp)"
screen -S $1 -X hardcopy "$tmp"
grep -v ^$ "$tmp" | tail -n1
rm "$tmp"
}
并使用以下命令调用它:
$ screen_key_tail foo1
其中是您要定位的会话foo1
的名称。screen