Toggle process with Python-script (kill when running, start when not running) - python

I'm currently running an OpenELEC (XBMC) installation on a Raspberry Pi and installed a tool named "Hyperion" which takes care of the connected Ambilight. I'm a total noob when it comes to Python-programming, so here's my question:
How can I run a script that checks if a process with a specific string in its name is running and:
kill the process when it's running
start the process when it's not running
The goal of this is to have one script that toggles the Ambilight. Any idea how to achieve this?

You may want to have a look at the subprocess module which can run shell commands from Python. For instance, have a look at this answer. You can then get the stdout from the shell command to a variable. I suspect you are going to need the pidof shell command.
The basic idea would be along the lines of:
import subprocess
try:
subprocess.check_output(["pidof", "-s", "-x", "hyperiond"])
except subprocess.CalledProcessError:
# spawn the process using a shell command with subprocess.Popen
subprocess.Popen("hyperiond")
else:
# kill the process using a shell command with subprocess.call
subprocess.call("kill %s" % output)
I've tested this code in Ubuntu with bash as the process and it works as expected. In your comments you note that you are getting file not found errors. You can try putting the complete path to pidof in your check_output call. This can be found using which pidof from the terminal. The code for my system would then become
subprocess.check_output(["/bin/pidof", "-s", "-x", "hyperiond"])
Your path may differ. On windows adding shell=True to the check_output arguments fixes this issue but I don't think this is relevant for Linux.

Thanks so much for your help #will-hart, I finally got it working. Needed to change some details because the script kept saying that "output" is not defined. Here's how it now looks like:
#!/usr/bin/env python
import subprocess
from subprocess import call
try:
subprocess.check_output(["pidof", "hyperiond"])
except subprocess.CalledProcessError:
subprocess.Popen(["/storage/hyperion/bin/hyperiond.sh", "/storage/.config/hyperion.config.json"])
else:
subprocess.call(["killall", "hyperiond"])

Related

Running a shell script using Subprocess in Python does not produce output

I am trying to run a shell script using through Python using subprocess.Popen().
The shell script just has the following lines:
#!/bin/sh
echo Hello World
Following is the Python code:
print("RUNNNING SHELL SCRIPT NOW")
shellscript = subprocess.Popen(['km/example/example1/source/test.sh'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
shellscript.wait()
for line in shellscript.stdout.readlines():
print(line)
print("SHELL SCRIPT RUN ENDED")
However, on running this, I am only getting the following output:
RUNNNING SHELL SCRIPT NOW
SHELL SCRIPT RUN ENDED
i.e. I am not getting the shell script output in between these 2 lines.
Moreover, when I remove the stderr=subprocess.PIPE part from the subprocess, I get the following output:
RUNNNING SHELL SCRIPT NOW
'km' is not defined as an internal or external command.
SHELL SCRIPT RUN ENDED
I am not able to understand how to resolve this, and run the shell script properly. Kindly guide. Thanks.
UPDATE:
I also tried the following change:
print("RUNNNING SHELL SCRIPT NOW")
shellscript = subprocess.Popen(['km/example/example1/source/test.sh'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out, err = shellscript.communicate()
print(out)
print("SHELL SCRIPT RUN ENDED")
I get the following output:
RUNNNING SHELL SCRIPT NOW
b''
SHELL SCRIPT RUN ENDED
The simple and straightforward fix is to not use bare Popen for this.
You also don't need a shell to run a subprocess; if the subprocess is a shell script, that subprocess itself will be a shell, but you don't need the help of the shell to run that script.
proc = subprocess.run(
['km/example/example1/source/test.sh'],
check=True, capture_output=True, text=True)
out = proc.stdout
If you really need to use Popen, you need to understand its processing model. But if you are just trying to get the job done, the simple answer is don't use Popen.
The error message actually looks like you are on Windows, and it tries to run km via cmd which thinks the slashes are option separators, not directory separators. Removing the shell=True avoids this complication, and just starts a process with the requested name. (This of course still requires that the file exists in the relative file name you are specifying. Perhaps see also What exactly is current working directory? and also perhaps switch to native Windows backslashes, with an r'...' string to prevent Python from trying to interpret the backslashes.)

Running bash script within python and wait for shell command to complete

I have the same question as here with python 3.x. I tried the solution provided but it does not work for me.
Python does not wait bash script to finish and prints "end" before bash script to be terminated.
I am on windows and have cygwin isntalled.
callBash.py:
import subprocess
print("start")
subprocess.call("sleep.sh",shell=True)
print("end")
sleep.sh:
#!/bin/bash
sleep 10
You never check the return value of subprocess.call, so it's likely that your script doesn't even start properly, maybe because of invalid PATH / CWD or permissions or something else.
Use subprocess.check_call instead, so that an exception is raised if your script fails to run. Exceptions are not so easy to miss.
Also if you have Python 3, subprocess.run(..., check=True) is newer and generally easier to work with.

Terminating started Popen subprocess in Python

I am on Windows and I am starting a new process using subprocess.Popen that I want to terminate at a certain point. However, the gui that I initiated is still visible. A minimal example would be starting the PNG viewer:
import subprocess
proc = subprocess.Popen(['start', 'test.png'], shell=True)
proc.kill()
After the kill() command the gui is still running and I have to close it manually.
As fas as I understood this can be solved on Linux by passing preexec_fn=os.setsid to Popen (see How to terminate a python subprocess launched with shell=True). Since the command os.setsid is specific to Linux I do not know how to realize that on Windows.
Another way would be to get rid of the shell=True, however, I don't know how to realize that because I have to pass the file name.
Any help would be greatly appreciated...
If you want to get rid of the shell=True you have to give the full path to the executable.
import subprocess
proc = subprocess.Popen('/full/path/start %s' % filename)
proc.kill()
start is an internal command: it requires cmd.exe (that you could start using shell=True or run directly). Popen() does not wait for start command to finish and start does not wait for the PNG viewer to exit -- by the time you call proc.kill(), start might have finished already.
You could try to run PNG viewer directly instead (you don't need to provide the full path if the corresponding exe-file can be found in the standard locations).
How to terminate a python subprocess launched with shell=True has a solution for Windows too (you could try it if PNG viewer starts child processes).

How to spawn detached background process on Linux in either bash or python

I have a long running python script on Linux, and in some situations it needs to execute a command to stop and restart itself. So, I would like to have an external script (either in bash or python) that executes command to restart the original script. Let me elaborate.
Suppose I have original_script.py. In original_script.py I have this in an infinite loop:
if some_error_condition:
somehow call external script external.sh or external.py
Let's suppose I can call external.sh and it contains this:
#!/bin/bash
command_to_restart_original_script
Finally, I know the command "command_to_restart_original_script". That isn't the problem. What need is the python command to "somehow call external script external.sh". I need the external script (which is a child process) to keep running as the parent process original_script.py is restarting, ie I need the child process to be detached/daemonized. How do I do this?
I found lots of suggestions in various places, but the only answer that worked for me was this:
How to launch and run external script in background?
import subprocess
subprocess.Popen(["nohup", "python", "test.py"])
In my case I ran a script called longrun.sh so the actual command is:
import subprocess
subprocess.Popen(["nohup", "/bin/bash", "longrun.sh"])
I tested this using this run.py:
import subprocess
subprocess.Popen(["nohup", "/bin/bash", "longrun.sh"])
print "Done!"
and I verified (using ps -ax | grep longrun) that longrun.sh does indeed run in the background long after run.py exits.

"script command" and logging in python

I have a python app that has lots of outputs on the screen which can be used for debugging. out of all the logging techniques, "script" command works well for me because I can see the output on the screen as well as logging it. I want to include that at the beginning of my python app to run automatically and log everything, when I do, however, the python program doesn't run. as soon as I type exit at the terminal (which stops script logging) the app starts working. The command I'm using is:
command="script /tmp/appdebug/debug.txt"
os.system(command)
I have also tried script -q but the same issue is there. Would appreciate any help.
Cheers
Well, I did find the answer for anyone who is interested:
https://stackoverflow.com/questions/15507602/logging-all-bash-in-and-out-with-script-command
and
Bash script: Using "script" command from a bash script for logging a session
I will keep this question as others might have the same issue and finding those answers wasn't exactly easy :)
Cheers
Try to use subprocess, like so:
from subprocess import Popen, PIPE
p = Popen(['script', '/tmp/appdebug/debug.txt'], stderr=PIPE, stdout=PIPE)
stdout, stderr = p.communicate()
script is a wrapper for a session of interactions. Even if it appears to terminate quickly after being started in a shell, this is not so; instead it starts a new shell in which you can interact so that everything is logged to a file.
What does this mean for you?
Your approach of using script cannot work. You start script using os.system which will wait for script to terminate before the next Python statement is executed. script's work will only happen before it terminates (i. e. during the uninteresting waiting period of your Python program).
I propose to use script -c yourprog.py yourprog.log instead. This will execute and wrap the yourprog.py and the session will be stored in yourprog.log.

Categories