I would like to ask for help with the python-crontab module. I have a simple shell script to record an internet radio stream using the curl command. I want to schedule recordings ahead by scheduling them in a crontab. I found the python-crontab module that alows me to write directly to crontab. But each time I schedule a new recording the older crontab entry is over-written. Is it possible to write persistant crontab entries using the python-crontab?
I simplified my code to demonstrate my problem:
from crontab import CronTab
def get_recording_parameters(Min,Hour,day,month,job_number):
radio_cron = CronTab()
cmd = "sh /home/pifik/Documents/record_radio.sh"
cron_job = radio_cron.new(cmd, comment='job_'+str(job_number))
cron_job.setall(Min, Hour, day, month, None)
radio_cron.write()
If I run it with the following parameters: get_recording_parameters(0,22,23,12,1), and check the crontab in Terminal with the crontab -l command I get 0 22 23 12 * sh /home/pifik/Documents/record_radio.sh # job_1.
If I run it again with different parameters, for example: get_recording_parameters(10,23,25,12,2) and check the crontab with crontab -l I get 10 23 25 12 * sh /home/pifik/Documents/record_radio.sh # job_2, the job 1 is overwritten.
I tried to change the 3rd line of code to radio_cron = CronTab(tabfile='/home/pifik/Documents/filename.tab') and it helps that all new entries are appended in the filename.tab but nothing is written to the crontab.
I am running Ubuntu 14.04 and Python 3.4.3.
It looks like each time you add a job and write it back out it's overwriting what was already in the crontab. I read the documentation and I can't make heads or tails out of it. It seems you should be able to read in what was already there and add to it but for the life of me I can't figure out how from the docs.
You can get around that issue by re-working it as a class that puts together all the jobs before it writes them back out. Of course that paints you into the same corner you're already in which is appending doesn't work (unless you lay in the old entries again before writing):
#!/home/sklassen/py/try-pycrontab/env/bin/python
from crontab import CronTab
class CronSet:
def __init__(self):
self._crontab = CronTab()
def add_job(self, min, hour, day, month, job_number):
cmd = "sh /home/pifik/Documents/record_radio.sh"
job = self._crontab.new(cmd, comment='job'+str(job_number))
job.setall(min, hour, day, month, None)
def save(self):
self._crontab.write()
def main():
c = CronSet()
c.add_job(0, 22, 23, 12, 1)
c.add_job(0, 23, 23, 12, 2)
c.save()
if __name__ == '__main__':
main()
# running 'crontab -l' produces the following
# 0 22 23 12 * sh /home/pifik/Documents/record_radio.sh # job1
# 0 23 23 12 * sh /home/pifik/Documents/record_radio.sh # job2
I modified Steven's code to make it work for me.
In order not to lose the previously scheduled crontab jobs I create a cron_jobs.txt file and copy all existing scheduled jobs from crontab to cron_jobs.txt with the subprocess call "crontab -l > /home/pifik/Documents/cron_jobs.txt". Every time I do it I overwrite everything in the file. Then I create a new recording job and append it to the cron_jobs.txt file. After that I overwrite the crontab by running "subprocess.call('crontab /home/pifik/Documents/cron_jobs.txt', shell=True)".
This is a workaround to make it work but I am still interested to know if it is possible to use the python-crontab module to append new jobs directly without the need for the cron_jobs.txt file. Arguably, the way I do it now, it could be also done without the module but the module makes it easier for me to further manage the jobs by activating or deactivating them and deleting the expired jobs. I will put some tkinter GUI on it and I'll be done for now.
import subprocess
from crontab import CronTab
class CronSet:
def __init__(self):
self._crontab = CronTab(tabfile="/home/pifik/Documents/cron_jobs.txt")
def add_job(self, minute, hour, day, month, title):
subprocess.call('crontab -l > /home/pifik/Documents/cron_jobs.txt', shell=True)
choice=input('''1. Cesky Rozhlas 1
2. Cesky Rozhlas 2
Enter your choice (1 or 2): ''')
length = int(input("Enter the length of recording in minutes: "))*60
if choice ==str(1):
stream = "http://amp.cesnet.cz:8000/cro1-256.ogg"
else:
stream = "http://amp.cesnet.cz:8000/cro2-256.ogg"
cmd = "curl %s -m %i -o /home/pifik/Documents/Recordings/%s.ogg" %(stream, length, title)
job = self._crontab.new(cmd, comment=title)
job.setall(minute, hour, day, month, None)
def save(self):
self._crontab.write()
def main():
c = CronSet()
month = input("Enter month(1-12): ")
day = input("Enter day(1-31): ")
hour = input("Enter hour(0-24): ")
minute = input("Enter minute(0-59): ")
title = input("Enter title of recording: ")
c.add_job(minute, hour, day, month, title)
c.save()
subprocess.call('crontab /home/pifik/Documents/cron_jobs.txt', shell=True)
if __name__ == '__main__':
main()
The issue is that you haven't specified any user or filename for CronTab to load from. So it doesn't. The bug is that it writes out the empty crontab to the user by default, even if you don't specify the user.
The existing documentation says:
from crontab import CronTab
empty_cron = CronTab()
my_user_cron = CronTab(user=True)
users_cron = CronTab(user='username')
Which is correct in that you're making an empty crontab. So I've gone ahead and committed a fix and a test to make sure it causes an error if you try and write an empty_cron without specifying the user or filename.
Please add user=True to your code to make it work as you expect.
Related
SO basically how do i do to set this to run at a specific time of the day ?
import winsound
from win10toast import ToastNotifier
def timer (reminder,seconds):
notificator=ToastNotifier()
notificator=ToastNotifier("Reminder",f"""Alarm will go off in (seconds) Seconds.""",duration=20
notificator.show_toast(f"Reminder",reminder,duration=20)
#alarm
frequency=2500
duration=1000
winsound.Beep(frequency,duration)
if __name__=="__main__":
words=input("What shall i be reminded of: ")
sec=int(input("Enter seconds: "))
timer(words,sec)
Could this be ? as i tried to write it but doesn t seem to work
import time
local_time = float(input())
local_time = local_time * 60
time.sleep(local_time)
Two possibilities from the top of my head:
[Linux] Use cron job https://help.ubuntu.com/community/CronHowto
[Any OS] Use scheduler https://schedule.readthedocs.io/en/stable/
[Windows] Scheduling a .py file on Task Scheduler in Windows 10
I am running a python file that, among other things, opens and runs a subprocess that runs the 'speedtest-cli --simple' command line. When executed normally, this works fine and returns the ping, download and upload, but when called with crontab, the values are blank. Values are normally only blank if an internet connection can not be established.
#!/usr/bin/env python
#Speed test opens a subprocess that runs the speed test from the command terminal, returning ping, download and upload speeds.
def speedTest():
cmd = 'speedtest-cli --simple' #Command to be run in terminal
envi = os.environ
response = subprocess.Popen(cmd, env=envi, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8')#Run subprocess
try: #Try to pull relevant data
ping = re.findall('Ping:\s(.*?)\s', response, re.MULTILINE)
download = re.findall('Download:\s(.*?)\s', response, re.MULTILINE)
upload = re.findall('Upload:\s(.*?)\s', response, re.MULTILINE)
except: #-2 Error code for Speedtest Error, entered if pull fails (NOTE: even if the pull isn't able to connect, the try should still succeed. This case should not be entered.)
print('failed')
ping = ['-2']
download = ['-2']
upload = ['-2']
errflag = -2 #Set errflag to -2 to track error
pass
if (ping == []): #-3 Error code for Speedtest Connection Error. Entered if pull is successful but connection could not be made.
ping.append('-3')
download.append('-3')
upload.append('-3')
errflag = -3 #Set errflag to -3 to track error
return [ping[0],download[0],upload[0]] #Return ping, download, and upload.
the crontab that runs it is shown here
Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
* * * * * /home/pi/datatest.py >> out.txt 2>&1
Any suggestions on how to obtain output values in crontab would be much appreciated.
I made a python script on my raspberry - /home/pi/bin/script.py:
#!/usr/bin/python
from urllib2 import urlopen
from time import sleep
FILE = "publicip"
SITE = "http://ipecho.net/plain"
DELAY = 60 # seconds
def get_ip():
while True:
# wait for DELAY seconds
sleep(DELAY)
# get my current public ip
try:
ip = urlopen(SITE).read()
except IOError:
continue
# compare with the one in file
file_value = open(FILE).read()
if ip != file_value: # if they are not equal
open(FILE, "w").write(ip) # update the ip in file
if __name__ == "__main__":
get_ip()
It's propose is to get my public ip and store it in a file.
I need this script run in a loop so it can update the file as soon
the ip changes.
If the power fail I want it to run when the raspberry restarts. So,
I updated the /etc/rc.local file:
#!/bin/sh -e
/home/pi/bin/script.py
exit 0
After that I used sudo reboot to restart the raspberry. I'm using PuTTY
from a windows computer to connect to the raspberry. After logging in again
I used ps -e | grep script.py to see if my script was runnig but it was
not. Then I runned the script manually and it worked!
What would you do to solve this problem?
An alternative for running in the cron or init, it to use a userspace monitor.
This tutorial is great showing supervisor.
It is really easy to use.
apt-get install supervisor
service supervisor restart
add to /etc/supervisor/conf.d/ip_update.conf
[program:ip_update]
command=/home/pi/bin/script.py
autostart=true
autorestart=true
stderr_logfile=/var/log/ip_update.err.log
stdout_logfile=/var/log/ip_update.out.log
and you can still use supervisorctl to manage it:
$ supervisorctl
> restart ip_update
First verify your script execution's permission, if it's have a execution permission. After that, you need use & after command of script in (runs to infinite loops), try:
#!/bin/sh
/home/pi/bin/script.py &
More details in raspbian documentation.
Your other option is to use cron
sudo crontab -e will open the crontab for you
you can set your script to run as often as you like and if you put in the entry:
#reboot /home/pi/bin/script.py
it should run during the boot sequence
other non numeric options are:
#yearly Run once a year, "0 0 1 1 *".
#annually (same as #yearly)
#monthly Run once a month, "0 0 1 * *".
#weekly Run once a week, "0 0 * * 0".
#daily Run once a day, "0 0 * * *".
#midnight (same as #daily)
#hourly Run once an hour, "0 * * * *"
The standard entry is:
# Minute Hour Day of Month Month Day of Week Command
0 * * * * /home/pi/bin/script.py
#Once an hour on the hour
* * * * * /home/pi/bin/script.py
#Every minute
Edit:
with reference to your comment, performing it with cron, means that you should take out the timing within your code, as that is what cron is doing. So you would end up up with something like:
#!/usr/bin/python
from urllib2 import urlopen
FILE = "publicip"
SITE = "http://ipecho.net/plain"
def get_ip():
try:
ip = urlopen(SITE).read()
except IOError:
continue
# compare with the one in file
file_value = open(FILE).read()
if ip != file_value: # if they are not equal
open(FILE, "w").write(ip) # update the ip in file
if __name__ == "__main__":
get_ip()
With reference to your existing code, I notice that you are never closing the file, just an endless loop of opening it with read and then opening it with write, I'm not sure quite how python will handle that offhand but it certainly isn't good practice.
Pasting my python code below:
from crontab import CronTab
tab = CronTab()
cmd1 = 'actual command'
cron_job = tab.new(cmd)
cron_job.minute.every(1)
cron_job.enable()
tab.write_to_user(user=True)
I am setting these cronjob based on some user input on an app. So overtime a user clicks submit, I want to create a new cronjob under the same crontab file (since its a webapp, it runs under the context of a standard user).
But it seems every time a user clicks submit, my crontab overwrites the previous task. Am I missing something here?
The way you are recreating the tab variable on each request does not append to the crontab in question; it overwrites it.
If you pass user=True to the CronTab constructor you will be able to append to the existing crontab:
from crontab import CronTab
tab = CronTab(user=True)
cmd1 = 'echo Hello'
cmd2 = 'echo CronTab'
job1 = tab.new(cmd1)
job1.minute.every(1)
job1.enable()
job2 = tab.new(cmd2)
job2.hour.every(1)
job2.enable()
print list(tab.commands) # ['echo Hello', 'echo CronTab']
# Explicitly specify we want the user's crontab:
tab2 = CronTab(user=True)
print list(tab2.commands) # ['echo Hello', 'echo CronTab']
I have a problem I'd like you to help me to solve.
I am working in Python and I want to do the following:
call an SGE batch script on a server
see if it works correctly
do something
What I do now is approx the following:
import subprocess
try:
tmp = subprocess.call(qsub ....)
if tmp != 0:
error_handler_1()
else:
correct_routine()
except:
error_handler_2()
My problem is that once the script is sent to SGE, my python script interpret it as a success and keeps working as if it finished.
Do you have any suggestion about how could I make the python code wait for the actual processing result of the SGE script ?
Ah, btw I tried using qrsh but I don't have permission to use it on the SGE
Thanks!
From your code you want the program to wait for job to finish and return code, right? If so, the qsub sync option is likely what you want:
http://gridscheduler.sourceforge.net/htmlman/htmlman1/qsub.html
Additional Answer for an easier processing:
By using the python drmaa module : link which allows a more complete processing with SGE.
A functioning code provided in the documentation is here: [provided you put a sleeper.sh script in the same directory]
please notice that the -b n option is needed to execute a .sh script, otherwise it expects a binary by default like explained here
import drmaa
import os
def main():
"""Submit a job.
Note, need file called sleeper.sh in current directory.
"""
s = drmaa.Session()
s.initialize()
print 'Creating job template'
jt = s.createJobTemplate()
jt.remoteCommand = os.getcwd()+'/sleeper.sh'
jt.args = ['42','Simon says:']
jt.joinFiles=False
jt.nativeSpecification ="-m abe -M mymail -q so-el6 -b n"
jobid = s.runJob(jt)
print 'Your job has been submitted with id ' + jobid
retval = s.wait(jobid, drmaa.Session.TIMEOUT_WAIT_FOREVER)
print('Job: {0} finished with status {1}'.format(retval.jobId, retval.hasExited))
print 'Cleaning up'
s.deleteJobTemplate(jt)
s.exit()
if __name__=='__main__':
main()