How to stop a process but not the whole script - python

I have a CLI program in python that will run files. However, if a program is run that never stops, I want the user to be able to 'kill' that program, but not the whole script. The input for stopping the script could be the push of a certain key on the keyboard, I really have no clue how to do this and I couldn't find any answers here. I am using a os.system process, here is a snippet of my code and an example:
if os.path.isfile('../file'):
cmd = os.path.join(os.getcwd(), '../file')
os.system('{} {}'.format('python', cmd))
print('Process finished')
And file contains the following code:
while True:
print("Do stuff")
Please note that I cannot edit 'file' and I want to be able to stop the os.system process with a key pressed without killing the entire main script.

You can encapsulate your block of code in a while loop that will run when the user enters text, and ends when a user enters an empty string (i.e. pressing the Enter or Return key).
while True:
i = input("Enter text (or Enter to quit): ")
if not i:
break
if os.path.isfile(selectedDir+'/'+file):
cmd = os.path.join(os.getcwd(), selectedDir+'/'+file)
os.system('{} {}'.format('python', cmd))
print('Process finished')
print("Your input:", i)
print("While loop has exited")
Alternatively, you can use try and except to handle an exception, and simply continue your program by using pass without error handling:
try:
if os.path.isfile(selectedDir+'/'+file):
cmd = os.path.join(os.getcwd(), selectedDir+'/'+file)
os.system('{} {}'.format('python', cmd))
print('Process finished')
except Exception:
# Your script will continue
pass

from subprocess import Popen,PIPE
is_running = False
if os.path.isfile('../file'):
cmd = os.path.join(os.getcwd(), '../file')
#os.system('{} {}'.format('python', cmd))
proc = Popen(["python3", "-m", cmd],stdout=PIPE, stderr=PIPE)
is_running = False
while is_running:
try:
output, error = proc.communicate()
except KeyboardError:
is_running = False
#Here if Ctrl+C pressed you can exit the program with next line -> Uncomment
#proc.kill()
#IF you want the script to continue after exiting the program all you have to do is uncomment the PASS in next line
#pass
print('Process finished')
You can modify this to check any Key pressed. I'll tag that info here;
To Check For Specific Keyboard Input
Another answer, an overkill, but will do your job

Related

How do I avoid creating multiple python process w/ os.system?

When the 2 consecutive if statements executes, my python program shuts down. But that's not what I want. I want it to loop again and not exit the script. The problem that I found with simply looping is that python processes are created every time the os.system(command) line runs. I've individually tried the following:
os.exit()
sys.exit()
def kill_process():
pid = os.getpid()
sig = signal.SIGKILL
os.kill(pid, sig)
All of those options were individually paired with a os.system("python3 script.py"), yet none of these did the trick. Every scenario simply exits the script.
How do I make it so that when os.system(command) is passed that it just loops again without killing/exiting the script and without creating another python process everytime?
Here's the function in question:
def bluetoothLoop():
while True:
time.sleep(5)
BT_state = subprocess.run(['''system_profiler SPBluetoothDataType'''], shell=True, capture_output=True, encoding="utf", errors="ignore")
BT_state = BT_state.stdout
sound = "Blow"
title = "TURN OFF BLUETOOTH"
message = "-------------------------"
if "State: On" in BT_state and not " Connected:" in BT_state:
time.sleep(1)
BT_state = subprocess.run(['''system_profiler SPBluetoothDataType'''], shell=True, capture_output=True, encoding="utf", errors="ignore")
BT_state = BT_state.stdout
if "State: On" in BT_state and not " Connected:" in BT_state:
command = f'''
osascript -e 'display notification "{message}" with title "{title}" sound name "{sound}"'
'''
os.system(command)
os.exit()
time.sleep(1)
notify.restart()
Thanks a bunch, I've been struggling with this for a while now.

Running else loop once

So i have a script with the code
import os
import subprocess
import psutil
def checkIfProcessRunning(processName):
'''
Check if there is any running process that contains the given name processName.
'''
#Iterate over the all the running process
for proc in psutil.process_iter():
try:
# Check if process name contains the given name string.
if processName.lower() in proc.name().lower():
return True
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
return False;
Then after executes
while True:
if checkIfProcessRunning('TDR'):
print('TDR (tdr.exe) is running')
else:
print('TDR (tdr.exe) is not running')
subprocess.call("cmd /c data.vbs") # Executes other code
This whole script detects if the process tdr.exe is open or not, when the code detects that it isn't open i want it to open some other code but i want it to only do it once instead of looping.
Help me understand, the issue here is that after calling your "other code" a.k.a data.vbs while keeps on executing it? Add a break after subprocess.call("cmd /c data.vbs").
Or structure it like this:
while True:
if not checkIfProcessRunning('TDR'):
break
print('TDR (tdr.exe) is running')
# Once we find TDR is dead, we execute our other code
print('TDR (tdr.exe) is not running')
subprocess.call("cmd /c data.vbs") # Executes other code
I hope this helps :D

How to kill a subprocess after 50 seconds?

I guess I have several questions covering multiple issues. So first, the code:
import subprocess
username = input ("Enter username: ")
subprocess.call(["python3", "sherlock", username])
Popen.terminate()
I am using subprocess to run the sherlock program using python3. The issue is, for whatever reason, the sherlock program does not exit when it is done. I'm not sure whether this is an issue with sherlock, or on my side. To be honest, I don't care, and just want to kill it on my side after giving it 50 seconds to run, as that's how long it takes to run.
I did some reading, and as far as I can tell, I should use the Popen.terminate() to kill it? I know the command needs more to run, I need to tell it what to kill... That's where I'm stuck. I'm not sure how to determine the running process, and then to kill it. 95% of my program occurs after sherlock runs, so it's not making it very far, so I need to just kill it and let my program continue on its way.
Not sure how to proceed from here...
UPDATE: Okay,so ive determined that the program finishes on its own, without having to kill it, but the problem is that once the program finishes, the python script does not continue. it just sits there doing nothing. is there a way to force it to move on to the next line of the script?
try something like this:
import subprocess
username = input ("Enter username: ")
try:
ret = subprocess.call(["python3", "sherlock", username], timeout=50)
except subprocess.TimeoutExpired:
# TODO: manage exception
pass
From the official documentation
Popen.communicate(input=None, timeout=None)
Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate.
I think this function is doing exactly what you want. You should pass 50 seconds to timeout parameter. A sample code snippet is below, again from documentation:
proc = subprocess.Popen(...)
try:
outs, errs = proc.communicate(timeout=50)
except TimeoutExpired:
proc.kill()
outs, errs = proc.communicate()
Some processes hang waiting on data from stdin. In that case, close that pipe and do your own wait for termination. Some processes catch signals and may need a less subtle kick to get them to exit.
import subprocess
username = input ("Enter username: ")
process = subprocess.Popen(["python3", "sherlock", username],
stdin=subprocess.PIPE)
process.stdin.close()
try:
process.wait(50)
except subprocess.TimeoutExpired:
# timeout expired, be nice
process.terminate()
try:
process.wait(2)
except subprocess.TimeoutExpired:
# be mean
process.kill()
try:
process.wait(2)
except TimeoutExpired:
# zombies, run!
print("sherlock could not be killed!")
import subprocess
username = input ("Enter username: ")
a_child_process = subprocess.Popen(args=["python3", "sherlock", username], stdout=subprocess.PIPE)
subprocess.Popen.terminate(a_child_process)

Is there a way to never exit on KeyboardInterrupt in python?

I'm creating sort of a interactive command line in python. I have something like this:
def complete_menu():
while True:
cmd = input('cmd> ')
if cmd == "help":
print('help')
elif cmd == "?":
print('?')
When the user presses CTRL-C, instead of exiting the program I'm trying to make it so that it prints "please type exit to exit" and goes back to the while True. I have something like this at the moment:
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print('Please use exit to exit')
complete_menu()
Although this works, there is a number of issues. For one, when CTRL-C is pressed the first time, it prints out the text and works perfectly. However, the second time the user presses CTRL-C, it exists with a bunch of messy text like any other program after pressing CTRL-C. Can this be fixed?
The better way to do this is to register a signal handler:
import signal
def handler(signum, frame):
print("Please use exit to exit")
# or: just call sys.exit("goodbye")
...
def main():
signal.signal(signal.SIGINT, handler) # prevent "crashing" with ctrl+C
...
if __name__ == "__main__":
main()
Now when a Ctrl+C is received in your code, instead of a KeyboardInterrupt exception being raised, the function handler will be executed. This is a basic example, customize the code within handler to do what you want.
Note: My recommendation is to actually let the user exit with Ctrl+C, i.e. execute any cleanup code that you might need to run and then call sys.exit here. Programs that require a stronger signal to kill are annoying.

How to interrupt a subprocess launched by sh?

I want to process output from a subprocess and decide to terminate that subprocess once I received sufficient output. My program logic decides when we have enough input.
Example: Wait for a Udev event
try:
for event in sh.udevadm('monitor', _iter=True):
if event matches usb stick added:
print("Ok, I will work with that USB stick you just plugged in!")
break
except:
pass
print("I copy stuff on your USB stick now!")
The 'break' terminates the process but I cannot catch the exception:
What is the correct way to terminate a subprocess or how can I handle the exception?
Maybe you're better off using subprocess.Popen directly instead of sh library.
Something similar to:
$ cat udevtest.py
import subprocess
try:
proc = subprocess.Popen(["udevadm", "monitor"], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
while True:
line = proc.stdout.readline()
if line == "" or len(line) == 0:
break # EOF
if line.find("pci0000") != -1: # your trigger
print("TRIGGER %s" % line.strip())
break
proc.terminate()
proc.wait()
except Exception, e:
print(e)
Found a way using interactive callbacks.
http://amoffat.github.io/sh/#interactive-callbacks
Pretty neat, I think.
def wait_for_plugged_in_drive(self):
print("Plugin an external HDD or USB stick.")
sh.udevadm.monitor(_out=self._scan_and_terminate).wait()
def _scan_and_terminate(self, line, stdin, process):
match = re.search('add\\s+/.*/block/sd./(sd..)', line)
if match is not None:
self.device = '/dev/' + match.group(1)
print("USB stick recognized {0}".format(self.device))
process.terminate()
return True

Categories