I have a Python code like this.
try:
while some_cond_is_still_true:
....
except KeyboardInterrupt: # Handle CTRL+C
...
While the KeyboardInterrupt is handled fine if I run the python script by myself, it is not handled if I run it as another user using su, like this.
su <some_other_user> -c 'python myprogram.py <args>'
How can I solve this problem?
The su command creates a new interactive shell and executes the command inside it.
When you use option -c (--command) the su command creates a new session with the user indicated in the command. To solve this use the option --session-command.
In this case the command will be this:
su user_name --session-command 'python myprogram.py <args>'
in this case you should be able to catch CTRL+C interrupt.
What The Bndr said is true. To solve this problem you can use the signal module to register a new callback function that will be called at SIGTERM signal.
In it you can either raise a KeyboardInterrupt() or set a global variable that controls the program's flow to False.
You can also try registering the default callback again, which does raise the KeyboardInterrupt() exception.
This should make the newly spawned shell propagate the signal correctly. If it doesn't, then you should choose the shell which will launch the script through su, because something is wrong with the one called at this moment. Or check your execute line at the top of your script.
Try playing with it with combinations of calls to the script like:
#! /usr/bin/env python
or
#! /usr/bin/python
or
#! /bin/sh python
or
#! /bin/bash python
and so on, and see what works. This assumes your script is made executable and you do not have to call Python interpreter directly. If you plan to distribute your script though, be careful what you leave there. Different distributions will react differently and some even may not have the problem you are currently experiencing.
If you use su <user> -c ... so an other shell in the conext of the specified user will be established.
I think: if you hit CTRL+C it depends on the shell, if the interrupt signal will reach the python scripts.
Try su <user> -c 'watch ls' to see, if CTRL + C will stop watching ls.
Not sure, what you trying to do with your script, but if you "only" like to abort an running script, you can do the following (alternatively to CTRL + C):
Stop this process (the users shell process) by pressing CRTL + Z. The Job number and the stopped process is displayed.
Now you can kill the process using the job number, like kill %1
Related
I have Python script bgservice.py and I want it to run all the time, because it is part of the web service I build. How can I make it run continuously even after I logout SSH?
Run nohup python bgservice.py & to get the script to ignore the hangup signal and keep running. Output will be put in nohup.out.
Ideally, you'd run your script with something like supervise so that it can be restarted if (when) it dies.
If you've already started the process, and don't want to kill it and restart under nohup, you can send it to the background, then disown it.
Ctrl+Z (suspend the process)
bg (restart the process in the background
disown %1 (assuming this is job #1, use jobs to determine)
Running a Python Script in the Background
First, you need to add a shebang line in the Python script which looks like the following:
#!/usr/bin/env python3
This path is necessary if you have multiple versions of Python installed and /usr/bin/env will ensure that the first Python interpreter in your $$PATH environment variable is taken. You can also hardcode the path of your Python interpreter (e.g. #!/usr/bin/python3), but this is not flexible and not portable on other machines. Next, you’ll need to set the permissions of the file to allow execution:
chmod +x test.py
Now you can run the script with nohup which ignores the hangup signal. This means that you can close the terminal without stopping the execution. Also, don’t forget to add & so the script runs in the background:
nohup /path/to/test.py &
If you did not add a shebang to the file you can instead run the script with this command:
nohup python /path/to/test.py &
The output will be saved in the nohup.out file, unless you specify the output file like here:
nohup /path/to/test.py > output.log &
nohup python /path/to/test.py > output.log &
If you have redirected the output of the command somewhere else - including /dev/null - that's where it goes instead.
# doesn't create nohup.out
nohup command >/dev/null 2>&1
If you're using nohup, that probably means you want to run the command in the background by putting another & on the end of the whole thing:
# runs in background, still doesn't create nohup.out
nohup command >/dev/null 2>&1 &
You can find the process and its process ID with this command:
ps ax | grep test.py
# or
# list of running processes Python
ps -fA | grep python
ps stands for process status
If you want to stop the execution, you can kill it with the kill command:
kill PID
You could also use GNU screen which just about every Linux/Unix system should have.
If you are on Ubuntu/Debian, its enhanced variant byobu is rather nice too.
You might consider turning your python script into a proper python daemon, as described here.
python-daemon is a good tool that can be used to run python scripts as a background daemon process rather than a forever running script. You will need to modify existing code a bit but its plain and simple.
If you are facing problems with python-daemon, there is another utility supervisor that will do the same for you, but in this case you wont have to write any code (or modify existing) as this is a out of the box solution for daemonizing processes.
Alternate answer: tmux
ssh into the remote machine
type tmux into cmd
start the process you want inside the tmux e.g. python3 main.py
leaving the tmux session by Ctrl+b then d
It is now safe to exit the remote machine. When you come back use tmux attach to re-enter tmux session.
If you want to start multiple sessions, name each session using Ctrl+b then $. then type your session name.
to list all session use tmux list-sessions
to attach a running session use tmux attach-session -t <session-name>.
You can nohup it, but I prefer screen.
Here is a simple solution inside python using a decorator:
import os, time
def daemon(func):
def wrapper(*args, **kwargs):
if os.fork(): return
func(*args, **kwargs)
os._exit(os.EX_OK)
return wrapper
#daemon
def my_func(count=10):
for i in range(0,count):
print('parent pid: %d' % os.getppid())
time.sleep(1)
my_func(count=10)
#still in parent thread
time.sleep(2)
#after 2 seconds the function my_func lives on is own
You can of course replace the content of your bgservice.py file in place of my_func.
Try this:
nohup python -u <your file name>.py >> <your log file>.log &
You can run above command in screen and come out of screen.
Now you can tail logs of your python script by: tail -f <your log file>.log
To kill you script, you can use ps -aux and kill commands.
The zsh shell has an option to make all background processes run with nohup.
In ~/.zshrc add the lines:
setopt nocheckjobs #don't warn about bg processes on exit
setopt nohup #don't kill bg processes on exit
Then you just need to run a process like so: python bgservice.py &, and you no longer need to use the nohup command.
I know not many people use zsh, but it's a really cool shell which I would recommend.
If what you need is that the process should run forever no matter whether you are logged in or not, consider running the process as a daemon.
supervisord is a great out of the box solution that can be used to daemonize any process. It has another controlling utility supervisorctl that can be used to monitor processes that are being run by supervisor.
You don't have to write any extra code or modify existing scripts to make this work. Moreover, verbose documentation makes this process much simpler.
After scratching my head for hours around python-daemon, supervisor is the solution that worked for me in minutes.
Hope this helps someone trying to make python-daemon work
You can also use Yapdi:
Basic usage:
import yapdi
daemon = yapdi.Daemon()
retcode = daemon.daemonize()
# This would run in daemon mode; output is not visible
if retcode == yapdi.OPERATION_SUCCESSFUL:
print('Hello Daemon')
Whenever I run this program, it completes the first function perfectly and then ends the program before doing anything else. How can I allow the other two functions to run?
import subprocess
subprocess.call(["su", "my_user"]) # runs perfectly
print("user switched to my_user") # does not run
subprocess.call(["cd", "../documents/my_code"]) # does not run
By the way, I am running this from a Linux terminal using ipython.
This isn't doing what you think it is. When you run su, just as the function says, this runs as a subprocess. That subprocess will start as my_user and immediately exit, but your Python process is unaffected. You will still be the original user.
You can feed commands into that subprocess, assuming you need to run things as that other user, but your process isn't going to change.
Followup
subprocess.call waits for the command to finish. The su command is going to create a new shell, logged in as the new user, and that shell will present a new prompt to you. You probably thought your Python script had ended and you were back at the original prompt, but that's not the case. The prompt you're seeing is from su, and you are the new user. If you press Ctrl-D, then the su will exit, your script will continue, and you'll see your script type "user switched to my_user". Thus, you are nested several shells deep and don't realize it. ;)
I am a newbie in Fabric, and want to run one command in a background, it is written in shell script, and I have to run that command via Fabric, so lets assume I have a command in shell script as:
#!/bin/bash/
java &
Consider this is a file named myfile.sh
Now in Fabric I am using this code to run my script as:
put('myfile.sh', '/root/temp/')
sudo('sh /root/temp/myfile.sh')
Now this should start the Java process in background but when I login to the Machine and see the jobs using jobs command, nothing is outputted.
Where is the problem please shed some light.
Use it with
run('nohup PATH_TO_JMETER/Jmetercommand & sleep 5; exit 0)
maybe the process exists before you return. when you type in java, normally it shows up help message and exits. Try a sleep statement or something that lingers. and if you want to run it in the background, you could also append & to the sudo call
I use run("screen -d -m sh /root/temp/myfile.sh",pty=False). This starts a new screen session in detached mode, which will continue running after the connection is lost. I use the pty=False option because I found that when connecting to several hosts, the process would not be started in all of them without this option.
I want to run a shell script that runs a python program and shutdowns after the program is done. Here is what I wrote
#!/bin/bash
python program
sudo shutdown -h now
This just shutdowns the system without waiting for the program to complete. Is there a different command to use that waits for the program to complete?
What you have in your example should actually only shutdown once the python command has completed, unless the python program forks or backgrounds early.
Another way to run it would be to make the shutdown conditional upon the success of the first command
python command && sudo shutdown -h now
Of course this still will not help you if the python program does anything like forking or daemonizing. Simply try running the python script alone and take note if control returns immediately to the console or not.
You could run the command halt to stop your system:
#!/bin/sh
python program
sudo halt
The python program is running first, and halt would run after its completion (you might test the exit code of your python program). If it does not behave like expected, try to understand why. You could add a logger command before the halt to write something in the system logs.
Alternatively, you can use command substitution like this:
$(command to run your program)
The script waits until the wrapped command finishes before moving onto the next one!
#!/bin/sh
$(python program.py)
sudo shutdown -P 0
My friend is in a macOS environment and he wanted to call os.system('exit') at the end of his python script to make the terminal close. It doesn't. This doesn't surprise me but I would like to know what exactly is going on between the python script and the terminal when this call is made.
In my mental simulation the terminal should have to tell you that there are still running jobs, but that doesn't happen either.
As a side question : will some less common terminals close when a process calls this?
read the help:
Execute the command (a string) in a subshell.
A subshell is launched, and exit is run in that subshell.
To exit the enclosing terminal, you have to kill the parent. One way to do it is:
os.system("kill -9 %d"%(os.getppid())
The system function starts another shell to execute a command. So in this case your Python scripts starts a shell and runs "exit" command in there, which makes that process exit. However, the Python script itself, including a terminal where it is running, continues to run. If the intent is to kill the terminal, you have to get the parent process ID and send a signal requesting it to stop. That will kill both Python script and a terminal.
Remember that system first spawns/forks a sub-shell to execute its commands. In effect, you are asking only the sub-shell to exit.