我正在编写一个Python程序,用于在Linux服务器上运行用户上传的任意代码(因此,在最坏的情况下,就是不安全,错误和崩溃的代码)。除了安全性问题外,我的目标是确定代码(可能以任何语言编写,编译或解释的)是否将正确的内容写入stdout
,stderr
以及是否将给定输入的其他文件写入程序的stdin
。之后,我需要向用户显示结果。
目前,我的解决办法是使用产卵子进程subprocess.Popen(...)
与文件句柄stdout
,stderr
和stdin
。后面的文件stdin
句柄包含了操作过程中的程序读取输入,并且该程序已终止后,将stdout
和stderr
文件的读取,并检查正确性。
这种方法在其他方面可以完美地起作用,但是当我显示结果时,我无法组合给定的输入和输出,因此输入将出现在与从终端运行程序时相同的位置。即对于像这样的程序
print "Hello."
name = raw_input("Type your name: ")
print "Nice to meet you, %s!" % (name)
stdout
运行后,包含程序的文件内容将为:
Hello.
Type your name:
Nice to meet you, Anonymous!
鉴于包含的文件的内容stdin
为Anonymous<LF>
。因此,简而言之,对于给定的示例代码(以及等效的 任何
其他代码),我想要实现如下结果:
Hello.
Type your name: Anonymous
Nice to meet you, Anonymous!
因此,问题在于检测程序何时等待输入。
我尝试了以下方法来解决问题:
Popen.communicate(…)
这允许父进程沿着管道单独发送数据,但是只能被调用一次,因此不适合具有多个输出和输入的程序-
正如可以从文档中推断出的那样。
直接从Popen.stdout和Popen.stderr读取并写入Popen.stdin
文档对此提出警告,并且在程序开始等待输入时,Popen.stdout
s.read()
和.readline()
调用似乎无限阻塞。
使用select.select(...)
,看是否文件句柄准备好I
/ O
这似乎没有任何改善。显然,管道始终可以读取或写入,因此select.select(...)
在这里没有太大帮助。
如该答案所建议,我尝试创建一个单独的Thread()来存储从读取stdout
到Queue()的结果。要求用户输入的行之前的输出行显示得很好,但是程序开始等待用户输入的行("Type your name: "
在上面的示例中)从未被读取。
按照这里的指示,我试图pty.openpty()
用主文件和从文件描述符创建一个伪终端。在那之后,我已经给奴隶的文件描述符作为参数subprocess.Popen(...)
调用的stdout
,stderr
和stdin
参数。读取以打开的主文件描述符os.fdopen(...)
产生的结果与使用不同线程的结果相同:要求输入的行不会被读取。
编辑: 使用@Antti
Haapala的示例来pty.fork()
创建子进程,而不是subprocess.Popen(...)
让我也阅读了创建的输出raw_input(...)
。
我也试过了read()
,read_nonblocking()
和readline()
方法(记录在这里)与Pexpect的催生了一个过程的,但最好的结果,我用了read_nonblocking()
,
是和以前一样:与输出线希望用户输入的东西不前阅读。
相同与创建的PTY pty.fork()
:苛刻的输入行 并 得到读。
编辑: 利用sys.stdout.write(...)
和sys.stdout.flush()
替代的print
荷兰国际集团在我的 掌握
程序,该程序创建的孩子,似乎解决提示行没有得到展示-它实际上得到了在这两种情况下阅读,虽然。
我也尝试过select.poll(...)
,但是似乎管道或PTY主文件描述符总是可以编写。
read()
可以替换glibc中的系统调用包装程序,以将输入传递给主程序。但是,这不适用于静态链接程序或汇编程序。(尽管现在考虑到这一点,任何这样的调用都可以从源代码中截获,并用已修补的版本替换read()
-可能仍然难以实现。)read()
syscall传达给程序可能是疯狂的…我认为PTY是必经之路,因为它伪造了一个终端,并且交互式程序在各处的终端上运行。问题是,如何?
您是否已经注意到,如果stdout是terminal(isatty),则raw_input将提示字符串写入stderr;如果stdout不是终端,那么提示符也会写入stdout,但是stdout将处于完全缓冲模式。
在tty上使用stdout
write(1, "Hello.\n", 7) = 7
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
write(2, "Type your name: ", 16) = 16
fstat(0, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb114059000
read(0, "abc\n", 1024) = 4
write(1, "Nice to meet you, abc!\n", 23) = 23
使用stdout不在tty上
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff8d9d3410) = -1 ENOTTY (Inappropriate ioctl for device)
# oops, python noticed that stdout is NOTTY.
fstat(0, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f29895f0000
read(0, "abc\n", 1024) = 4
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f29891c4bd0}, {0x451f62, [], SA_RESTORER, 0x7f29891c4bd0}, 8) = 0
write(1, "Hello.\nType your name: Nice to m"..., 46) = 46
# squeeze all output at the same time into stdout... pfft.
因此,所有写入都同时被压缩到stdout中。读取输入后的情况更糟。
因此,真正的解决方案是使用pty。但是,您做错了。为了使pty起作用,必须使用pty.fork()命令而不是子进程。(这将非常棘手)。我有一些这样的工作代码:
import os
import tty
import pty
program = "python"
# command name in argv[0]
argv = [ "python", "foo.py" ]
pid, master_fd = pty.fork()
# we are in the child process
if pid == pty.CHILD:
# execute the program
os.execlp(program, *argv)
# else we are still in the parent, and pty.fork returned the pid of
# the child. Now you can read, write in master_fd, or use select:
# rfds, wfds, xfds = select.select([master_fd], [], [], timeout)
请注意,根据子程序设置的终端模式,可能会有不同类型的换行符等。
现在有关“等待输入”的问题,由于总是可以写入伪终端,因此无法真正解决。字符将在缓冲区中等待。同样,在阻塞之前,管道始终允许写入多达4K或32K或其他一些实现定义的数量。一种丑陋的方法是跟踪程序,并在程序进入读取系统调用时注意到它,fd
= 0; 另一种方法是使用替换的“
read()”系统调用制作一个C模块,并在动态链接程序的glibc之前将其链接(如果可执行文件是静态链接的,或者直接通过汇编程序使用系统调用则失败…),并且然后将在执行read(0,…)系统调用时向python发送信号。总而言之,可能完全不值得麻烦。
如果你想等待 process::Child 完成,就必须调用 Child::wait,这会返回一个 process::ExitStatus。 use std::process::Command; fn main() { let mut child = Command::new("sleep").arg("5").spawn().unwrap(); let _result = chi
问题内容: 对于子进程,和函数可用于暂停当前进程的执行,直到子进程退出。但是此功能不能用于非子进程。 是否有另一个函数可以等待任何进程的退出? 问题答案: 什么都不等于。通常的做法是使用轮询,寻找返回值-1和的,表明进程已经一去不复返了。
我希望能够在后台启动一大批工作,而不使用bash脚本,并继续在同一个内核上工作。这有可能吗?我对架构变化持开放态度,但我的库的最终用户可能不是很复杂。 表示ipython单元 python 3.7。6从JupyterLab内部
我正在编写一个运行许多子进程的脚本。脚本应该能够运行它们所有的内容,然后在一个循环中检查如果它们已经完成。 不是开始一个,等它完成,然后开始下一个。 但是像这样,脚本等待完成。 我如何运行多个子进程,而不是等待它们完成,而是检查它们是否完成(例如,通过检查返回代码)?
问题内容: 我找不到如何测量线程等待锁定的时间。我必须确定一个线程是否正在等待锁定超过1秒,如果需要,则运行另一个线程。谢谢! 问题答案: 试试这个:
问题内容: 我正在Windows 8 / XP上使用Python 2.7。 我有一个程序A,它使用以下代码运行另一个程序B: B运行批处理脚本C。C是运行时间较长的脚本,即使C尚未完成,我也希望B退出。我已经使用以下代码(在B中)完成了此操作: 当我运行B时,它可以按预期工作。但是,当我运行A时,我希望它在B退出时退出。但是,即使B已经退出,A也会等到C退出。关于正在发生的事情以及可能的解决方案的