Reputation: 1246
I'm currently working on automating some processes via Django. For this purpose I have a shell script, that performs certain tasks for me. My webinterface has a "Start Process"-Button and a "Stop Process"-Button.
I came up with the idea to use the process id of the shell script to kill the process when I want to abort it. My start.sh
looks like this:
pid=$$;
source ./stop.sh || true
echo $pid > "./processid.txt";
#do the actual stuff
And my stop.sh
looks like this:
pid=$(head -n 1 ./processid.txt)
kill $pid
What this does, it takes the process id and temporarily stores it in a variable, then stops the script, if before the script had be running, and then saves the process id to the processid.txt
file. My stop.sh just takes this id out of the file and kills the process.
This does work from the commandline... however it doesn't when I execute it from my python code. I call it like this:
def start_bot():
popencommand = "sh -c \"cd ~/thetargetfolder && ./start.sh\""
Popen(popencommand, shell=True)
def stop_bot():
popencommand = "sh -c \"cd ~/thetargetfolder && ./stop.sh\""
Popen(popencommand, shell=True)
For some reason, this doesn't seem to work. I suspect it has something to do with that the process id that I get in the start.sh
is not the correct one when calling the python script...
Upvotes: 2
Views: 514
Reputation: 295403
When you use subprocess.Popen("sh -c 'cd /somewhere && ./whatever'", shell=True)
, you're telling Python to kick off a chain of events that runs three processes:
shell=True
makes Python run sh -c '...yourcode...'
, it starts a first copy of sh
requested by the shell=True
sh
runs the code sh -c 'cd /somewhere && ./whatever'
, because that's the string you gave Python, so it then starts a second copy of sh
.sh
runs cd /somewhere && ./whatever
. If ./whatever
is a shell script, that means it starts a third shell.This is a lot of unnecessary complexity! There's no reason to have more than one shell -- the one that runs ./whatever
, invoked by the operating system based on its shebang (#!/bin/sh
, or #!/usr/bin/env bash
, or so forth).
Some of this you can eliminate by dropping down to only having one shell, the run that runs your scripts. Make sure the scripts have valid shebangs (#!/usr/bin/env bash
or similar) and are executable (chmod +x start stop
).
def start_bot():
subprocess.call(
[os.path.join(os.path.expanduser('~/thetargetfolder'), './start')],
cwd=os.path.expanduser('~/thetargetfolder')
)
def stop_bot():
subprocess.call(
[os.path.join(os.path.expanduser('~/thetargetfolder'), './stop')],
cwd=os.path.expanduser('~/thetargetfolder')
)
Finally, you can do even better by not using a shell for this at all, but just writing your logic directly in Python, or (even better than that!) relying on your operating system's process supervisor (most modern Linux distros ship systemd for the purpose; whereas MacOS ships with launchd). If you define yourprogram.service
, then systemctl start yourprogram
starts the script running, and systemctl stop yourprogram
kills it -- and you can configure it to automatically start on boot, or on a timer, or whenever any program connects to a preopened socket, or otherwise however you like.
Upvotes: 4