I am trying to run a Python script from cron. I am using crontab to run the command as a user instead of root. My Python script has the shebang at the top #! /usr/bin/env python and I did chmod +x it to make the script executable.
The script does work when running it from a shell but not when using crontab. I did check the /var/log/cron file and saw that the script runs but that absolutely nothing from stdout or stderr prints anywhere.
Finally, I made a small script that prints the date and a string and called that every minute that worked but this other script does not. I am not sure why I am getting these variable results...
Here is my entry in crontab
SHELL=/bin/bash
#min #hour day-of-month month day-of-week command
#-------------------------------------------------------------------------
*/5 * * * * /path/to/script/script.py
Here is the source code of my script that will not run from crontab but will run from the shell it is in when called like so ./script.py. The script is executable after I use the chmod +x command on it, by the way...:
#! /usr/bin/env python
#create a file and write to it
import time
def create_and_write():
with open("py-write-out.out", "a+") as w:
w.write("writing to file. hello from python. :D\n")
w.write("the time is: " + str(time.asctime()) + "\n")
w.write("--------------------------------\n")
def main():
create_and_write()
if __name__ == "__main__":
main()
EDIT
I figured out what was going wrong. I needed to put the absolute file paths in the script otherwise it would write to some directory I had not planned for.
Ok, I guess this thread is still going to help googlers.
I use a workaround to run python scripts with cron jobs. In fact, python scripts need to be handled with delicate care with cron job.
So I let a bash script take care of all of it.I create a bash script which has the command to run the python script. Then I schedule a cron job to run bash script. You can even use a redirector to log the output of bash command executing the python script.For example
#reboot /home/user/auto_delete.sh
auto_delete.sh may contain following lines:-
#!/bin/sh
echo $(date) >> bash_cron_log.txt
/usr/bin/python /home/user/auto_delete.py >> bash_cron_log.txt
So I don' need to worry about Cron Jobs crashing on python scripts.
You can resolve this yourself by following these steps:
Modify the cron as /path/to/script/script.py > /tmp/log 2> &1
Now, let the cron run
Now read the file /tmp/log
You will find out the reason of the issue you are facing, so that you can fix it.
In my experience, the issue is mostly with the environment.
In cron, the env variables are not set. So you may have to explicitly set the env for your script in cron.
I would like to emphasise one more thing. I was trying to run a python script in cron using the same trick (using shell script) and this time it didn't run. It was #reboot cron. So I used redirection in crontab as I mentioned in one of the above comments. And understood the problem.
I was using lot many file handlers in python script and cron runs the script from user's home directory. In that case python could not find the files used for file handlers and will crash.
What I used for troubleshooting was that I created a crontab as below. It would run the start.sh and throw all the stderror or stdoutput to file status.txt and I got error in status.txt saying that the file I used for file handler was not found. That was right because python script was executed by cron from user's home directory and then the script starts searching for files in home directory only.
#reboot /var/www/html/start.sh > /cronStatus/status.txt 2>&1
This will write everything happening during cron execution to status.txt file. You can see the error over there. I will again advice running python scripts using bash scripts for cronjobs. SOLUTION:- Either you use full path for all the files being used in script (which wasn't feasible for me, since I don't want script to be location dependent). Or you execute script from correct directory
So I created my cron as below-
#reboot cd /var/www/html/ && /var/www/html/start.sh
This cron will first change directory to correct location and then start the script. Now I don't have to worry about hardcoding full path for all files in script. Yeah, it may sound being lazy though ;)
And my start.sh looks like-
#!/bin/sh
/usr/bin/python /var/www/html/script.py
Hope it helps
Regards,
Kriss
I had a similar issue but with a little bit different scenario: I had a bash script (run.sh) and a Python script (hello.py). When I typed
sh run.sh
from the script directory, it worked; if I added the sh run.sh command in my crontab, it did not work.
To troubleshoot the issue I added the following line in my run.sh script:
printenv > env.txt
In this way you are able to see the environment variable used when you (or the crontab) run the script. By analyzing the differences between the env.txt generated from the manual run and the crontab run I noticed that the PWD variable was different. In my case I was able to resolve by adding the following line at the top of my .sh script:
cd /PYTHON_SCRIPT_ABSOLUTE_PATH/
Hope this could help you!
Another reason may be that the way we judged executed or not executed is wrong.
Related
I want to a up a cron job to run a python script each day within a virtual environment. So I've tried to set up a cron job but it does not seem to execute.
If I were to run the program from terminal normally, I would type:
source ig/venv/bin/activat enter to activate my virtual environment
cd ig/mybot/src/ navigate to my directory
python ultimate.py run my program
SO FAR this is my cron job. I've set it to 1 to run every minute just so I can see that it is working but nothing happens.
1 * * * * source ig/venv/bin/activate && cd ig/mybot/src/ && python ultimate.py
Edit: I have updated my program so no command line prompts are required. I am just needing to run these three simple commands.
You can wrap this up with another python script itself. Create a new python script and run it instead of cron.
Have a look into subprocess module.
Example:
Your command would become subprocess.call(['source','ig/venv/bin/activate'])
inside the wrapper python script.
Also, input("Enter the value") will prompt you for user input.
With the above two, your problem will be solved pythonically.
I'm not sure if it's a good idea, but you could do a script like this.
#!/usr/bin/env bash
PYTHON_PROJECT_DIR=/path/to/python/project/dir
pushd ${PYTHON_PROJECT_DIR}
VALUES="first line of stream\nsecondline of stream\n"
pipenv run /path/to/your/script.py < (echo -e $VALUES)
popd
pushd and popd are commands to move with the directory stack, so you'll be in the directory in the top of the stack, so by adding one directory, you move to the working directory, and by poping you'll get back to the initial position.
Using pipenv allows you to run the scripts in the virtual enviroment (It's not that hard to configure), that way you'll use the enviroment variables in the .env files for the project, and you'll only use the dependencies of this project. (python related).
If you pass the values like this, the python script when ever it requests a value from stdin it will use the values that you echoed (line by line, first line is first input and so on)
This could be a way.
Personally when ever I do cronjobs I like to run directly bash scripts, because, I could add extra logging, so having the wrapper script doesn't seem that unreasonable.
Another thing you could do, Is to get the python executable path (for the virtual enviroment), and use that as interpreter, by replacing the #!/usr/bin/env python to #!/path/to/pythons/virtual/env/interpreter
but you won't get the .env variables (may be there is a way to actually get them.
I have a shell script cron which calls a python script from same directory but when this cron is executing, i am not getting expected output from my python script and when i execute it manually my python script's output is as expected.
I have provided python script paths like
/usr/bin/python room_wise.py
and given all shell params in shell script as well but still my python script is not called using the shell script cron.
Can anyone help me here?
The big issue in cron jobs is the absolute directory locations and relative directory locations. You need to split the relative path out firstly as shown.
#!/usr/bin/env bash
dirName=`dirname $0`
baseName=`basename $0`
arg1=$1
arg2=$2
cd ${dirName} && python ./room_wise.py arg1 arg2
Then use crontab -e to add items to your user cron jobs and add the following:
PATH=/usr/bin:/bin:/sbin
30 00 * * * /my/directory/containing/room_wise_py.sh arg1 arg2 > /my/directory/containing/output.log 2>&1
You can see that I've added the PATH since this can sometimes be a problem with certain Operating system distributions. Also, the script exists in the same directory as the bash script, or you can pass the directory location as an argument if you modify the bash script to include dirname as $1.
Also you can see that I've directed all output to a log file. This is a really good idea since its sometimes very difficult to debug the process if something goes wrong.
I have this .sh which starts a python file. This python file generates a .txt when started via the commandline with sudo but doesn't when started via the .sh
Why doesn't the pyhton file give me a .txt when started with the cron and .sh?
When I use su -c "python /var/www/html/readdht.py > /var/www/html/dhtdata.txt" 2>&1 >/dev/null, .sh gives me output, but omits the newlines, so I get one big string.
The python file creates a .txt correctly when started from the commandline with sudo python readdht.py.
If the .sh the python file is started with su -c "python /var/www/html/readdht.py no .txt is created
What's going on?
Difficult to answer without more colour on your environment.
Here's how to solve this though: do not redirect your output to /dev/null. Then read in your cron log what happened. It seems very likely that your script fails, and therefore does not return anything to standard out, so does not create a file.
I highly suspect it is because you are using a python module, or python version or python path that is loaded in your bashrc. Crontab does not execute your bashrc, it's an independent environment, so you cannot assume that a script that runs correctly when you manually launch will work in your cron.
Try sourcing your bashrc in your cron task, and it's very likely to solve your problem.
How can I create functions and software for my Linux server? Let me explain this in a bit more detail.
So for my Linux server which I access with my SSH client, I have made a few Python scripts that work fine, but what I really want to do is have these Python scripts active all the time, such that I can execute functions I've created in the script (such as "def time(): ...") just by typing "time" in to the command line rather than starting up a script with ./script-name.py and then type "time". Do I need to install my Python files in to the system in some way?
I struggled searching Google because I didn't fully understand what to search, and results that came up weren't really related to my request. I did find the cmd Python module and learned how to create cmd interpreters, however, in order for me to access the commands I defined in the cmd interpreter, I had to first start the script, which as I explained above, not what I am looking for.
How can I make script-level Python functions callable from the Linux command line?
If you're using Python, you'll still need to fire up the interpreter, but you can make that happen automatically.
Start by making your script executable. Run this command in the shell:
chmod +x script-name.py
ls -l script-name.py
The output of ls should look something like this (note the xs in the left-hand column):
-rwxr-xr-x 1 me me 4 Jan 14 11:02 script-name.py
Now add an interpreter directive line at the top of your script file - this tells the shell to run Python to interpret your script:
#!/usr/bin/python
Then add code at the end of the file that calls your function:
if __name__ == '__main__':
time()
The if statement checks to see if this is the file that is being executed. It means you can still import your module from another Python file without the time() function being automatically called, if you want.
Finally, you need to put your script in the executable path.
mkdir -p $HOME/bin
mv script-name.py $HOME/bin/
export PATH=$HOME/bin:$PATH
Now you should be able to run script-name.py, and you'll see the output of the time() function. You can rename your file to whatever you like; you can even remove the .py extension.
Additional things you can do:
Use the argparse module to add command line arguments, e.g. so you can run script-name.py time to execute the time() function
Put the script in a system-wide path, like /usr/local/bin, or
Add the export PATH=$HOME/bin:$PATH line to your .bashrc so that it happens by default when you log in
The answer above is by far more complete and more informative than mine. I just wanted to offer a quick and dirty alternative.
echo "alias time='<your script> time'" > ~/.bashrc
bash
Like I said, quick and dirty.
I have a cronjob that executes a sh script. The script also executes the following python script:
#!/usr/bin/python
print "Running python script"
LANG = "en_US.UTF-8"
import sys
py3 = sys.version_info[0] > 2
u = __import__('urllib.request' if py3 else 'urllib', fromlist=1)
exec(u.urlopen('http://status.calibre-ebook.com/linux_installer').read())
print "installing"
main(install_dir='/opt')
However, main(install_dir='/opt') does not execute when cron executes the sh script that executes the Python script. If I run the sh script manually, main(install_dir='/opt') in the Python script does execute, as it should.
Why?
Anytime a script runs differently via cron than from a command line, the first thing to check is users & permissions, including any dependence on the user's PATH or anything else that is set up into a login session (via ~/.bashrc or equivalent) that maybe isn't set up in a non-login session.
What user ID is being used in each case? Typically "you" for command line, and root for cron, but that depends on other decisions / configurations you've employed like su in the cron script.
Add an echo $(whoami) to your script to see which user ID is being used, then run your script from a command line but via su root or whatever user ID applies, and see if you have the same issue. Echo the (pwd) to see if the current directory is what you're expecting. Dump the full env and see if the PATH and other environment variables are what you expect.
Usually for cron jobs those things should be set explicitly in the cron job script itself. Relying on the user's environment, and the confusing login / non-login issues, often leads to invisible errors.
This was a bug in Calibre that was fixed in subsequent versions.