我想知道如何创建一个完全交互式的终端。我正在创建一个这样的交互式bash终端:
fds = open(ptsname(fdm), O_RDWR);
if (fork())
{
....
}
else
{
...
ioctl(0, TIOCSCTTY, 1);
// Execution of the program
{
char *child_av[] = {"/bin/sh", "-i", NULL};
rc = execvp(child_av[0], child_av);
}
// if Error...
return -1;
}
然后我可以继续使用read()/ write()发送命令并接收输出。我的问题是如何自动执行CTRL和SHIFT键? Python的子进程可以做到这一点,因此绝对有可能。
process = subprocess.Popen(..) ...
process.send_signal(signal.SIGINT)
from subprocess import Popen, PIPE
shift_a_sequence = '''keydown Shift_L
key A
keyup Shift_L
'''
def keypress(sequence):
p = Popen(['xte'], stdin=PIPE)
p.communicate(input=sequence)`
答案 0 :(得分:0)
您似乎拥有所需的大多数框架:
要详细了解问题出在哪里,您需要提供Minimal, Complete, and Verifiable example,而不是模糊的描述。
答案 1 :(得分:0)
这里有四个独立的问题:
在接收到Ctrl-C作为输入时(在熟模式下),由终端的线路规程触发信号。您可以通过发送0x03来做到这一点,它等于清除了第7位的大写ASCII'C':
write(fdm, "\x03", 1);
process.send_signal
process = subprocess.Popen(..) ... process.send_signal(signal.SIGINT)
这只是执行fork
+ kill
,因此它与您为实现#1所做的任何事情都不相关。您可以在strace
中看到它:
$ cat foo.py
import signal
import subprocess
process = subprocess.Popen(["sleep", "10"])
process.send_signal(signal.SIGINT)
$ strace -f -eclone,kill,execve python foo.py
execve("/usr/bin/python", ["python", "foo.py"], 0x7ffe4d179458 /* 30 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa0f6e14a10) = 12917
strace: Process 12917 attached
[pid 12917] execve("/usr/bin/sleep", ["sleep", "10"], 0x7ffe6a45efc0 /* 30 vars */) = -1 ENOENT (No such file or directory)
[pid 12917] execve("/bin/sleep", ["sleep", "10"], 0x7ffe6a45efc0 /* 30 vars */ <unfinished ...>
[pid 12916] kill(12917, SIGINT) = 0
[pid 12917] <... execve resumed> ) = 0
[pid 12917] --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=12916, si_uid=1000} ---
[pid 12917] +++ killed by SIGINT +++
Esc很简单:您只需发送其ASCII值即可。这是man ascii
:
033 27 1B ESC (escape)
Shift和Control是修饰符,因此您实际上并未发送它们。您只需修改发送的字符。 #1已经介绍了如何在简单的ascii字符上执行Ctrl键。
要进行移位,您应该只使用适当的字符串函数将要关注的字符大写。清除/设置位6的传统硬件逻辑不适用于Unicode字符,但是如果您愿意,可以使用'c' == 'C'+0x20
。
请注意,这不适用于箭头键之类的东西,在这种情况下,您需要发送与要模拟的终端相对应的不同ANSI转义代码。
为完整起见,Alt / Meta具有两种形式:传统上将bit 8设置(多数出于Unicode原因而弃用),或Meta-As-Escape,您只需将两个字节ESC
x
发送给{ {1}}。
Alt+x
xte
这将打开一个X11实用程序,该实用程序模拟X11按键序列。如果您在XTerm中运行该程序并且不切换焦点,则希望最终会在您启动的终端中结束,但这完全不能保证。绝对不像#3。
但是,如果您想这样做,则可以使用C中的from subprocess import Popen, PIPE
shift_a_sequence = '''keydown Shift_L
key A
keyup Shift_L
'''
def keypress(sequence):
p = Popen(['xte'], stdin=PIPE)
p.communicate(input=sequence)`
,方式几乎相同,但这并不能帮助您完成任何用文字描述的事情。
您可以在此处找到根据代码发送改编的popen
的完整示例:
Ctrl+C
执行表明睡眠被中断,shell继续运行,最后退出:
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#define __USE_BSD
#include <termios.h>
#define guard(x) do { if((x)<0) { perror(#x); exit(1); } } while(0);
int main(void)
{
int fdm, fds, rc;
char input[150];
fdm = posix_openpt(O_RDWR);
guard(grantpt(fdm));
guard(unlockpt(fdm));
if (fork())
{
char* output = "sleep 60\n";
// Start a long sleep
write(fdm, output, strlen(output));
// Wait and send Ctrl-C to abort it after 1 second
sleep(1); write(fdm, "\x03", 1);
// Make sure shell is still alive
output = "echo 'Shell is still alive'\n";
write(fdm, output, strlen(output));
// Wait and send Ctrl-D to exit
sleep(1); write(fdm, "\x04", 1);
while((rc = read(fdm, input, sizeof(input)-1)) > 0) {
input[rc] = 0;
printf("From PTY:\n%s\n", input);
}
close(fdm);
wait(NULL);
}
else
{
setsid();
guard(fds = open(ptsname(fdm), O_RDWR));
close(fdm);
dup2(fds, 0);
dup2(fds, 1);
dup2(fds, 2);
close(fds);
execlp("sh", "sh", "-i", NULL);
}
return 0;
} // main