在新的终端窗口中从Python执行终端命令?

26

这里的目标是在现有shell中从一个现有的python文件中运行一个新的python文件。假设我有两个文件,aaa.py和bbb.py。为简单起见,假设aaa.py仅仅做了...

subprocess.call('python bbb.py', shell=True)

假设现在有一个名为bbb.py的Python文件,它包含了一些功能...

print 'It worked'

现在的目标是在终端1中运行aaa.py,并使其启动终端2中的bbb.py。我期望会存在类似下面的命令,但无法弄清楚。

subprocess.call_in_new_window('python bb.py', shell=True)

请查看我在这里的回答(相关):https://dev59.com/AGUo5IYBdhLWcg3wnwn2#20612529 - user1882644
3个回答

51

通常情况下,从shell中无法完成此操作。您需要运行终端程序本身或一些为您执行此操作的启动程序。而且每个终端程序的方法都不同。

在某些情况下,os.startfile可以实现您想要的效果,但这并不是普遍适用的。

另外,请注意,通常情况下,您实际上需要脚本的绝对路径,因为新的终端窗口将运行一个新的shell,因此不一定具有您相同的工作目录。但在示例中我会忽略这一点。


使用Windows cmd,最简单的方法是使用 start shell 命令。如果您使用 start 启动的是任何命令行程序,包括 python,它都将获得一个新的cmd窗口。因此,可以尝试以下命令:

subprocess.call('start /wait python bb.py', shell=True)

OS X有一个类似的命令open。这是一个真正的程序而不是一个shell命令,所以你不需要shell=True。但是,使用open运行命令行程序或脚本通常不会打开新的终端窗口。实际上,它的整个用意就是允许你像在Finder中双击一样运行程序,除非它是一个.command文件,否则不会在终端中运行任何内容。

因此,你可以创建一个临时的.command包装文件并打开它;像这样(未经测试):

with tempfile.NamedTemporaryFile(suffix='.command') as f:
    f.write('#!/bin/sh\npython bb.py\n')
    subprocess.call(['open', '-W', f.name])

或者,您可以显式地告诉open使用Terminal.app,像这样:

subprocess.call(['open', '-W', '-a', 'Terminal.app', 'python', '--args', 'bb.py'])

或者你可以通过AppleEvents脚本化Terminal.app。例如:

appscript.app('Terminal').do_script('python bb.py')

"do script"事件会打开一个新窗口,并将其参数作为命令运行。如果您想获得更详细的控制,请在AppleScript编辑器中打开脚本词典,看看您可以做些有趣的事情。


在Linux或其他*nix系统上... 好吧,有65,102个不同的桌面环境、启动器和终端程序。你需要在所有这些系统上工作吗?

使用gnome-terminal,只需再次运行终端即可获得一个新窗口,而-x参数可以让您指定初始命令,因此:

subprocess.call(['gnome-terminal', '-x', 'python bb.py'])

许多旧终端试图与 xterm 兼容,而 -e 也能实现同样的效果,因此:

subprocess.call(['xterm', '-e', 'python bb.py'])
subprocess.call(['rxvt', '-e', 'python bb.py'])
你如何知道用户正在使用哪个终端?很好的问题。你可以从自己开始遍历父进程链直到找到类似终端的东西。或者你可以假设每个人都有。或者你可以查看各种发行版如何配置默认终端并搜索所有这些终端。或者…

在OS X中,“--args”被用作终端应用程序的参数,而不是命令行应用程序(在这种情况下是Python)。因此,在这种情况下,subprocess.call启动一个运行Python的终端应用程序(忽略args)。我目前的解决方法是创建一个临时的.sh脚本,其中包含命令行和args,并将该脚本作为subprocess.call()中的参数来启动。有点笨拙,但我想不到更好的方法。 - Dexter Legaspi
非常好的答案,但我想知道您是否知道如何在无头Linux(例如AWS Ubuntu实例)上完成此操作(运行多个终端)?我有一个启动其他脚本的脚本,但是“gnome-terminal”参数会搞乱一切,因为无头服务器没有安装任何GUI终端。 - alfredox
帮了我很多。现在我再试着关闭终端,但这更困难了。我似乎无法杀死进程。如果您能扩展您的答案并提供如何关闭终端的说明,那将非常棒。 - Si Mon
1
我的唯一评论是,对于Windows系统,使用以下形式:subprocess.call('start "" {}'.format(cmd), shell=True)可以获得最佳结果。 - Frak
有没有办法在运行完成后保持终端窗口打开?(即返回到闪烁的光标命令行) - Praxiteles

17

这可能本应该是一条评论,但由于我还不能...

在Windows中,可以这样做:

subprocess.call('python bb.py', creationflags=subprocess.CREATE_NEW_CONSOLE)

3
有没有办法使终端窗口持久化,以便在命令完成后不会关闭? - Praxiteles
2
导入 os; os.system('start cmd /K python bb.py') ... 使用 /c 而不是 /K 会在完成后关闭新窗口。 - P S Solanki
请为Linux用户指定一个命令。 - heyom
我可以在调用第二个窗口后关闭第一个/主窗口吗?换句话说,假设我正在调用两个东西。第二个会等待第一个完成/结束。这不是我想要的方式。它们应该被异步调用。这可能吗? - akinuri

1
您无法轻易地实现这个想法,至少不是您所考虑的那么简单。我猜你是在谈论Mac,因为提到了“终端窗口”。
您可能可以使用X Window系统来实现它,但是您需要设置一堆东西,如X服务器、权限等。
如今,此类操作通常会违反正常的安全界限。假设您下载了一个像您所建议的那样运行的程序。它会引出一个窗口(终端)(对您不可见),它具有与您相同的特权。它继续读取您的所有目录和文件,并将它们发送给程序的发起者。这可能会让您感到不满意。整个时间,您认为自己在玩游戏,然后退出,第二个shell仍在运行。
Windows和shell有些脱节。

你不知道安全是如何运作的。 - oren revenge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接