Cron executing a sh script that executes a python script - python

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.

Related

How do I schedule the execution of a python script in linux with the at command?

My goal is to schedule the execution of a simple python script in Linux with the at command.
Problem: My python script is not executed at the specified time. Nothing happens, no error messages.
I have the following simple helloworld file:
#!/usr/bin/python3
print("hello world")
To schedule the job I type:
at now + 1 min
I provide the file I want to execute:
./helloworld
Typing atq I see that the job was scheduled nicely.
But it just happens...nothing.
I work on Kali Linux
I have given execution permission to the root user for that file
I can run the helloworld file from the command line
the root user has permission to run at commands (not listed in /etc/at.deny)
I can schedule the job echo "hello world" > message.txt. That works fine.
What I am not sure about:
is there something wrong with the shebang line?
I have checked /usr/bin/: python3 is in there and is linked to python3.9.
at does not write to the terminal (which may not even exist when the command runs). Instead,
The user will be mailed standard error and standard output from his commands, if any. Mail will be sent using the command sendmail(8). If at is executed from a su(1) shell, the owner of the login shell will receive the mail.
So if your command only writes to standard output, you'll need to check the local mailbox for the output of the command, using mail or some other mail client.
Otherwise, you can have your command explicitly write to some other known file to look for later.
It is better to keep the extension .py for your script since it is python.
Your file should be executable
$ chmod +x ./helloworld
For your needs, you can just type the full path to your script when running at.
If you want to use helloworld command, the folder that contains this script should be in PATH environment variable. Otherwise, the system just doesn't know where to find it. If you do this, you don't need to type ./ before the command or any path at all.
Nice article about PATH https://opensource.com/article/17/6/set-path-linux

How do I create a cronjob for these commands?

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.

Directing output from cron (calls) bash (calls) python3

I have a simple python script, test.py, which prints the date and time and then raises an error.
I have a bash function defined in .bash_profile and named test(), which calls the script with
$ python3 ~/test.py
Finally, I have a cron line set to call the test() bash function once a minute for testing with
test >> ~/$(date +\%Y-\%m-\%d_\%H:\%M:\%S).log 2>&1
When I run the python script or the bash function, I correctly get both the print and the error to the terminal. When cron calls the python script directory, it logs correctly. But when cron calls the bash function, nothing is written to the log file.
Question
How do I correctly direct the output of the python script to the log file when cron calls the bash function?
First of all test is a bad name for a function as almost all shells have test builtin and also there is external test command available in almost all systems.
Now, when you run something in cron, unlike starting of a login and/or interactive shell session no session startup script is read hence the function defined in ~/.bash_profile (source-d while starting login session) is not being available.
Note that, many systems do not use bash to run cron scripts, for example Ubuntu uses dash. Anyway, the test you are executing in cron is presumably that shell's builtin test command which will return exit code 1 without any argument.
Why not just put the following line directly in your crontab, and eliminate the test() bash function middleman?
* * * * 1 /usr/bin/python3 /home/myname/test.py >> ~/$(date +\%Y-\%m-\%d_\%H:\%M:\%S).log 2>&1

Python Script not running in cron

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.

Workaround Way To Install Numpy?

Right now I have a script which uses numpy that I want to run automatically on a server. When I ssh in and run it manually, it works fine. However, when I set it to run as a cron job, it can't find numpy. Apparently due to the shared server environment, the cron demon for whatever reason can't find numpy. I contacted the server host's tech support and they told me to set up a vps or get my own damn server. Is there any way to hack a workaround for this? Perhaps, by moving certain numpy files into the same directory as the script?
If you have numpy installed somewhere on the server, you can add it into the import path for python; at the beginning of your script, do something like this:
import sys
sys.path.append("/path/to/numpy")
import numpy
The cronjob runs with an empty environment. As such, it's either not using the same python binary as you are at the shell, or you have PYTHONPATH set, which it won't have under crontab.
You should run env -i HOME=$HOME sh to get a fascimile of the cronjob's environment. Set environment variables until your command works, and record them.
You can then set these in your crontab file, again using the env command, like:
* * * * * env PYTHONPATH=/my/pythonpath OTHERVAR=correct-value /path/to/mycommand
Processes invoked by the cron daemon have a minimal environment, generally consisting of $HOME, $LOGNAME and $SHELL.
It sounds like numpy is perhaps somewhere on your $PYTHONPATH? If so, you will need to specify that within the crontab line. Such as
/usr/bin/env PYTHONPATH=... <then the command to run>
If you are on a Linux system using vixie cron, then you can also specify global variables in your crontab by using lines such as
# my environment settings
PYTHONPATH = <path>
SOMETHING_ELSE = blah
<then my normal cron line>
See man -s 5 crontab
Your cron job is probably executing with a different python interpreter.
Log in as you (via ssh), and say which python. That will tell you where your python is. Then have your cron job execute that python interpreter to run your script, or chmod +x your script and put the path in a #! line at the top of the script.

Categories

Resources