Execute terminal command from python in new terminal window? - python

The goal here is to run a new python file in a new shell from and existing python file in an existing shell. Say i have two files, aaa.py and bbb.py. Lets say for simplicity that all aaa.py does is...
subprocess.call('python bbb.py', shell=True)
...and lets say that bbb.py does is...
print 'It worked'
Now the goal is to run aaa.py in terminal 1 and get it to launch bbb.py in terminal 2. I would expect something like the command below to exist, but can't figure it out.
subprocess.call_in_new_window('python bb.py', shell=True)

There's no way to do this in general from a shell. What you have to do is run the terminal program itself, or some launcher program that does so for you. And the way to do that is different for each terminal program.
In some cases, os.startfile will do what you want, but this isn't going to be universal.
Also, note in general, you're going to actually need an absolute path to your script, because the new terminal window will be running a new shell and therefore won't necessarily have your same working directory. But I'll ignore that for the examples.
With Windows cmd, the easiest way to do it is the start shell command. If the thing you start is any command-line program, including python, it will get a new cmd window. So, something like:
subprocess.call('start /wait python bb.py', shell=True)
OS X has a similar command, open. And it's a real program rather than a shell command, so you don't need shell=True. However, running a command-line program or script with open doesn't generally open a new terminal window. In fact, the whole point of it is to allow you to run programs as if they were being double-clicked in Finder, which never runs something in the terminal unless it's a .command file.
So, you can create a temporary .command wrapper file and open that; something like this (untested):
with tempfile.NamedTemporaryFile(suffix='.command') as f:
f.write('#!/bin/sh\npython bb.py\n')
subprocess.call(['open', '-W', f.name])
Alternatively, you can explicitly tell open to use Terminal.app, something like this:
subprocess.call(['open', '-W', '-a', 'Terminal.app', 'python', '--args', 'bb.py'])
Or you can script Terminal.app via AppleEvents. For example:
appscript.app('Terminal').do_script('python bb.py')
The "do script" event opens a new window and runs its argument as a command. If you want more detailed control, open the scripting dictionary in AppleScript Editor and see all the fun stuff you can do.
On Linux or other *nix systems… well, there are 65,102 different desktop environments, launchers, and terminal programs. Do you need to work on all of them?
With gnome-terminal, just running the terminal again gives you a new window, and the -x argument lets you specify an initial command, so:
subprocess.call(['gnome-terminal', '-x', 'python bb.py'])
Many older terminals try to be compatible with xterm, which does the same thing with -e, so:
subprocess.call(['xterm', '-e', 'python bb.py'])
subprocess.call(['rxvt', '-e', 'python bb.py'])
… etc.
How do you know which terminal the user is using? Good question. You could walk the like of parent processes from yourself until you find something that looks like a terminal. Or you could just assume everyone has xterm. Or you could look at how various distros configure a default terminal and search for all of them. Or…

This should probably be a comment, but since I can't yet...
In Windows , you can do:
subprocess.call('python bb.py', creationflags=subprocess.CREATE_NEW_CONSOLE)

You won't be able to make that happen, at least not as simply as you are thinking about it. I suspect that you are talking about a Mac because of "terminal window".
You might be able to make it happen using the X Window system, but you will need a bunch of things to be set up, X-servers, permissions, etc. to make that happen.
These days, such things usually violate normal security boundaries. Say you download a program that behaves as you suggest. It brings up a window (terminal) (invisible to you) that has the same privileges that you have. It proceeds to read all of your directories and files and ships them to the originator of the program. You might not be happy with that. The whole time, you think you are playing a game, then you quit, and the second shell keeps running.
Windows and shells are a bit disjoint.

Related

run new cygwin shell and kill it when I want

I've created an app that runs on cygwin, that will open some new shells and run python script on each of them. The problem started when I wanted to have control on the new shells and kill them at will. after a lot of digging I decided to use the following command:
subprocess.run('mintty.exe -t {} -h always -e {} &'.format(app_name, run_app_cmd), shell = True)
and later when I'll want to kill it use:
subprocess.run('kill -2 {}'.format(apps[app].shell_pid), shell = True).
it worked pretty well until I realized that A-L-O-T of times the new terminal gets stuck and doesn't respond, and I don't like it. I made some more digging and I found that while I thought that the python on the current mintty executes the command and opens the new terminal, what that actually happens is that the windows host opens the new mintty (the PPID of the new terminal is 1), and then probably the signal goes through some windows problems and etc.
the reason that I want each of the scripts in a separate terminal is that each of them have a lot of output, and I want them in different windows.
Now, after all this explanation, is there any way to prevent this? I don't want these stuck to become a part of my life...

Create a subprocess in a new shell that can be killed at any point

I want to run a terminal command in a separate shell, but I also want to be able to kill/terminate it at any time. I've read some answers here, and they say to do something like process = subprocess.Popen(args=['gnome-terminal', '--command=%s' % cmd]). The problem with this is, calling process.kill() or process.terminate() after the new shell has been opened does nothing. I want to be able to call a function and kill or terminate the new shell process.
The gnome-terminal command is not actually the terminal program; it's a launcher that talks to a factory and asks it to reuse an existing terminal program to open a new window or tab, or to create a new terminal program if necessary.
So, killing this launcher doesn't make any sense. And, even if it would tell you the PID of the actual terminal process, you wouldn't want to kill that, because it could kill a bunch of other terminal sessions.
You can use the --disable-factory flag to avoid this behavior:
Do not register with the activation name server, do not re-use an active terminal.
For more information on the details of gnome-terminal, you probably want to search or ask somewhere else (probably Super User, Unix & Linux, or Ask Ubuntu), but this should be all you need for your problem.

Running multiple Python scripts

I would like to create a simple Python program that will concurrently execute 2 independent scripts. For now, the two scripts just print a sequence of numbers but my intention is to use this program to concurrently run a few Twitter streaming programs in the future.
I suspect I need to use subprocess.Popen but I cannot quite get my head around what arguments I should put in there. There was a similar question on StackOverflow but the code provided there (pasted below) doesn't print anything. I will appreciate your help.
My files are:
thread1.py
thread2.py
import subprocess
subprocess.Popen(['screen', './thread1.py']))
subprocess.Popen(['screen', './thread2.py'])
Use supervisord
supervisord is process control system just for the purpose of running multiple command line scripts.
It features:
multiple controlled processes
autorestarting failed runs
log stdout and stderr output
starting scripts in order (using priority)
command line utility to view latest log output, stop, start, restart the processes
This solution works only on *nix based systems, it is not available on Windows.
As wanderlust mentioned, why do you want to do it this way and not via linux command line?
Otherwise, the solution you post is doing what it is meant to, i.e, you are doing this at the command line:
screen ./thread1.py
screen ./thread2.py
This will open a screen session and run the program and output within this screen session, such that you will not see the output on your terminal directly. To trouble shoot your output, just execute the scripts without the screen call:
import subprocess
subprocess.Popen(['./thread1.py'])
subprocess.Popen(['./thread2.py'])
Content of thread1.py:
#!/usr/bin/env python
def countToTen():
for i in range(10):
print i
countToTen()
Content of thread2.py:
#!/usr/bin/env python
def countToHundreds():
for i in range(10):
print i*100
countToHundreds()
Then don't forget to do this on the command line:
chmod u+x thread*.py
You can also just open several Command Prompt windows to run several Python programs at once - just run one in each of them:
In each Command Prompt window, go to the correct directory (such as C:/Python27) and then type 'python YourCodeNo1.py' in one Command Prompt window, 'python YourCodeNo2.py' in the next one ect. .
I'm currently running 3 codes at one time in this way, without slowing any of them down.

Python open default terminal, execute commands, keep open, AND then allow user-input

I'm wanting to open a terminal from a Python script (not one marked as executable, but actually doing python3 myscript.py to run it), have the terminal run commands, and then keep the terminal open and let the user type commands into it.
EDIT (as suggested): I am primarily needing this for Linux (I'm using Xubuntu, Ubuntu and stuff like that). It would be really nice to know Windows 7/8 and Mac methods, too, since I'd like a cross-platform solution in the long-run. Input for any system would be appreciated, however.
Just so people know some useful stuff pertaining to this, here's some code that may be difficult to come up with without some research. This doesn't allow user-input, but it does keep the window open. The code is specifically for Linux:
import subprocess, shlex;
myFilePathString="/home/asdf asdf/file.py";
params=shlex.split('x-terminal-emulator -e bash -c "python3 \''+myFilePathString+'\'; echo \'(Press any key to exit the terminal emulator.)\'; read -n 1 -s"');
subprocess.call(params);
To open it with the Python interpreter running afterward, which is about as good, if not better than what I'm looking for, try this:
import subprocess, shlex;
myFilePathString="/home/asdf asdf/file.py";
params=shlex.split('x-terminal-emulator -e bash -c "python3 -i \''+myFilePathString+'\'"');
subprocess.call(params);
I say these examples may take some time to come up with because passing parameters to bash, which is being opened within another command can be problematic without taking a few steps. Plus, you need to know to use to quotes in the right places, or else, for example, if there's a space in your file path, then you'll have problems and might not know why.
EDIT: For clarity (and part of the answer), I found out that there's a standard way to do this in Windows:
cmd /K [whatever your commands are]
So, if you don't know what I mean try that and see what happens. Here's the URL where I found the information: http://ss64.com/nt/cmd.html

launch external shell python instance in shell from python

I'd like to call a separate non-child python program from a python script and have it run externally in a new shell instance. The original python script doesn't need to be aware of the instance it launches, it shouldn't block when the launched process is running and shouldn't care if it dies. This is what I have tried which returns no error but seems to do nothing...
import subprocess
python_path = '/usr/bin/python'
args = [python_path, '&']
p = subprocess.Popen(args, shell=True)
What should I be doing differently
EDIT
The reason for doing this is I have an application with a built in version of python, I have written some python tools that should be run separately alongside this application but there is no assurance that the user will have python installed on their system outside the application with the builtin version I'm using. Because of this I can get the python binary path from the built in version programatically and I'd like to launch an external version of the built in python. This eliminates the need for the user to install python themselves. So in essence I need a simple way to call an external python script using my current running version of python programatically.
I don't need to catch any output into the original program, in fact once launched I'd like it to have nothing to do with the original program
EDIT 2
So it seems that my original question was very unclear so here are more details, I think I was trying to over simplify the question:
I'm running OSX but the code should also work on windows machines.
The main application that has a built in version of CPython is a compiled c++ application that ships with a python framework that it uses at runtime. You can launch the embedded version of this version of python by doing this in a Terminal window on OSX
/my_main_app/Contents/Frameworks/Python.framework/Versions/2.7/bin/python
From my main application I'd like to be able to run a command in the version of python embedded in the main app that launches an external copy of a python script using the above python version just like I would if I did the following command in a Terminal window. The new launched orphan process should have its own Terminal window so the user can interact with it.
/my_main_app/Contents/Frameworks/Python.framework/Versions/2.7/bin/python my_python_script
I would like the child python instance not to block the main application and I'd like it to have its own terminal window so the user can interact with it. The main application doesn't need to be aware of the child once its launched in any way. The only reason I would do this is to automate launching an external application using a Terminal for the user
If you're trying to launch a new terminal window to run a new Python in (which isn't what your question asks for, but from a comment it sounds like it's what you actually want):
You can't. At least not in a general-purpose, cross-platform way.
Python is just a command-line program that runs with whatever stdin/stdout/stderr it's given. If those happen to be from a terminal, then it's running in a terminal. It doesn't know anything about the terminal beyond that.
If you need to do this for some specific platform and some specific terminal program—e.g., Terminal.app on OS X, iTerm on OS X, the "DOS prompt" on Windows, gnome-terminal on any X11 system, etc.—that's generally doable, but the way to do it is by launching or scripting the terminal program and telling it to open a new window and run Python in that window. And, needless to say, they all have completely different ways of doing that.
And even then, it's not going to be possible in all cases. For example, if you ssh in to a remote machine and run Python on that machine, there is no way it can reach back to your machine and open a new terminal window.
On most platforms that have multiple possible terminals, you can write some heuristic code that figures out which terminal you're currently running under by just walking os.getppid() until you find something that looks like a terminal you know how to deal with (and if you get to init/launchd/etc. without finding one, then you weren't running in a terminal).
The problem is that you're running Python with the argument &. Python has no idea what to do with that. It's like typing this at the shell:
/usr/bin/python '&'
In fact, if you pay attention, you're almost certainly getting something like this through your stderr:
python: can't open file '&': [Errno 2] No such file or directory
… which is exactly what you'd get from doing the equivalent at the shell.
What you presumably wanted was the equivalent of this shell command:
/usr/bin/python &
But the & there isn't an argument at all, it's part of sh syntax. The subprocess module doesn't know anything about sh syntax, and you're telling it not to use a shell, so there's nobody to interpret that &.
You could tell subprocess to use a shell, so it can do this for you:
cmdline = '{} &'.format(python_path)
p = subprocess.Popen(cmdline, shell=True)
But really, there's no good reason to. Just opening a subprocess and not calling communicate or wait on it already effectively "puts it in the background", just like & does on the shell. So:
args = [python_path]
p = subprocess.Popen(args)
This will start a new Python interpreter that sits there running in the background, trying to use the same stdin/stdout/stderr as your parent. I'm not sure why you want that, but it's the same thing that using & in the shell would have done.
Actually I think there might be a solution to your problem, I found a useful solution at another question here.
This way subprocess.popen starts a new python shell instance and runs the second script from there. It worked perfectly for me on Windows 10.
You can try using screen command
with this command a new shell instance created and the current instance runs in the background.
# screen; python script1.py
After running above command, a new shell prompt will be seen where we can run another script and script1.py will be running in the background.
Hope it helps.

Categories

Resources