{"id":6401,"date":"2024-07-15T03:00:00","date_gmt":"2024-07-15T07:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=6401"},"modified":"2024-07-10T20:55:50","modified_gmt":"2024-07-11T00:55:50","slug":"automate-tasks-with-crontab","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=6401","title":{"rendered":"Automate Raspberry Pi tasks with crontab"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"6401\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div>\n<p>I have a Raspberry Pi at home that I use to automate several things. For example, my printer is old enough that it doesn\u2019t support networking, so it\u2019s connected to the Raspberry Pi, which shares the printer to my home network using CUPS. I also use the Raspberry Pi as an at-home web server to work on websites, and as a \u201cfile server\u201d of sorts by using SFTP from my file manager.<\/p>\n\n\n\n<p>This became somewhat less useful last year when my Internet provider \u201cupgraded\u201d the home WiFi router. The new router has an added security feature where it disconnects any device from the network after about two or three days. Without a network connection, I can\u2019t print, backup files, or work on web pages.<\/p>\n\n\n\n<p>Restarting the Raspberry Pi would put it back on the network. So I needed to find a way to restart the Raspberry Pi every other day. And I needed it to happen automatically, and gracefully, when I wasn\u2019t likely to use it. That\u2019s the perfect use case for <strong>cron<\/strong> and <strong>crontab<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"using-cron-and-crontab\">Using \u2018cron\u2019 and \u2018crontab\u2019<\/h2>\n\n\n\n<p>On Unix-like systems, <code>cron<\/code> is the system that executes tasks at specific times. The jobs that run, and <em>when<\/em> they run, are controlled using a special file called the <strong>crontab<\/strong>. Every user can create their own <strong>crontab<\/strong> to run tasks according to their own schedule.<\/p>\n\n\n\n<p>The <strong>crontab<\/strong> file is arranged like a plain text \u201ctable\u201d with fields that define the time, day, month, and day of the week to run the job. You can find detailed information about the <strong>crontab<\/strong> file in the <strong>crontab<\/strong>(1) online manual page, with this command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ man 5 crontab<\/code><\/pre>\n\n\n\n<p>The classic fields of the <strong>crontab<\/strong> file are:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Field<\/th><th>Range of values<\/th><\/tr><\/thead><tbody><tr><td>1. Minute<\/td><td>0 to 59<\/td><\/tr><tr><td>2. Hour<\/td><td>0 to 23<\/td><\/tr><tr><td>3. Day of month<\/td><td>1 to 31<\/td><\/tr><tr><td>4. Month<\/td><td>1 to 12<\/td><\/tr><tr><td>5. Day of week<\/td><td>0 to 6<\/td><\/tr><tr><td>6. Command to run<\/td><td>(enter the full command here)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Modern <strong>crontab<\/strong> implementations, like the one on Linux, also support extensions to these fields. For example, the Vixie <code>cron<\/code> implementation (by Paul Vixie) on most Linux distributions also accepts 0 to 7 as days of the week, where 0 and 7 are the same (Sunday). You can also use names for the months and days of the week, such as <code>sun<\/code> for Sunday or <code>jan<\/code> for January. But I like using numbers, so I\u2019ll use that here.<\/p>\n\n\n\n<p>For example, let\u2019s say you needed to run a <code>tar<\/code> backup of a website, every Sunday at 6:00 AM. You can create this <strong>crontab<\/strong> entry:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0 6 * * 0 \/bin\/tar czf $HOME\/website.tar.gz \/var\/www\/html<\/code><\/pre>\n\n\n\n<p>The first two fields indicate <code>0<\/code> for the minutes and <code>6<\/code> for the hour. The fifth field says <code>0<\/code> for the day of the week, which means \u201cSunday.\u201d The third and fourth fields are <code>*<\/code> to mean \u201cany day of the month\u201d and \u201cany month.\u201d Together, these five fields will execute the <code>tar<\/code> backup every Sunday at 6:00 AM.<\/p>\n\n\n\n<p>To enter the <strong>crontab<\/strong> file into the <code>cron<\/code> system, you need to use the <code>crontab<\/code> command. If your <strong>crontab<\/strong> file is called <code>jobs<\/code>, then you would type this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ crontab jobs<\/code><\/pre>\n\n\n\n<p>You can verify the contents of the <strong>crontab<\/strong> file with <code>crontab -l<\/code> to <em>list<\/em> (<code>-l<\/code>) the contents of the file. You can also erase your <strong>crontab<\/strong> from the system with <code>crontab -r<\/code> to <em>remove<\/em> (<code>-r<\/code>) the file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"scheduled-reboot\">Scheduled reboot<\/h2>\n\n\n\n<p>In my case, I needed to reboot the Raspberry Pi before the router took it off the network. One way to do this is with the <code>telinit<\/code> command, to change the \u201cSystem V\u201d run level. On Linux systems that run systemd, this executes the appropriate <code>systemctl<\/code> command. But I learned on original Unix systems, so I prefer <code>telinit<\/code>.<\/p>\n\n\n\n<p>To reboot every day at 7:00 AM, I started by creating this <strong>crontab<\/strong> file for the <strong>root<\/strong> user:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0 7 * * * \/sbin\/telinit 6<\/code><\/pre>\n\n\n\n<p>The first two fields are <code>0<\/code> for the minutes and <code>7<\/code> for the hour, with <code>*<\/code> for all of the other fields; this will run at 7:00 AM every day. I saved this file as <code>\/root\/jobs<\/code> and I saved it into the system with this command as <strong>root<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># crontab \/root\/jobs<\/code><\/pre>\n\n\n\n<p>But I didn\u2019t need to reboot every day; I only needed to reboot every <em>other<\/em> day. And Vixie <code>cron<\/code> has a neat syntax where you can specify <code>\/<\/code> and then a number to create an alternate schedule. For example, <code>0-30\/2<\/code> for \u201cminutes\u201d would run a job every other minute: \u201c:00,\u201d \u201c:02,\u201d \u201c:04,\u201d \u2026 and so on until \u201c:26,\u201d \u201c:28,\u201d and \u201c:30.\u201d This runs on even minutes because \u201cminute\u201d can have values from 0 to 59, and zero is an even number.<\/p>\n\n\n\n<p>To reboot my Raspberry Pi every <em>other<\/em> day, I updated the <strong>crontab<\/strong> entry to use <code>*\/2<\/code> for \u201cday of the month.\u201d This runs the job every other day: 1, 3, 5, \u2026 until 27, 29, and 31. These are odd days because \u201cday of the month\u201d is a value between 1 and 31, and 1 is an odd number.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0 7 *\/2 * * \/sbin\/telinit 6<\/code><\/pre>\n\n\n\n<p>With this entry, my Raspberry Pi rebooted itself at 7:00 AM every <em>other<\/em> day of the month: June 1, June 3, June 5, \u2026 and so on for the rest of the month.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"combining-jobs\">Combining jobs<\/h2>\n\n\n\n<p>I figured as long as the Raspberry Pi would reboot every other day, I might as well apply updates at the same time. For that, I wanted to run the <code>dnf<\/code> command to install any updates, then use <code>telinit<\/code> to reboot the system.<\/p>\n\n\n\n<p>When you need to combine multiple tasks into a single <strong>crontab<\/strong> job, I recommend collecting the commands into a script. In my case, I created this Bash script called <code>\/root\/bin\/update_reboot<\/code> to run <code>dnf<\/code> and <code>telinit<\/code>, plus keep a log of what it did:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# apply updates, reboot anyway\n\nlogdir=\/root\/log\n&#91; -d $logdir ] || mkdir -p $logdir\n\ntoday=$(date '+%d')\n\ndnf -y update &gt; $logdir\/dnf_update.$today.log\ntelinit 6<\/code><\/pre>\n\n\n\n<p>In this script, I wanted to keep a private log in <code>\/root\/log<\/code> to capture the output from the <code>dnf<\/code> command. The two lines that set the <code>logdir<\/code> variable and run <code>mkdir<\/code> ensure that my <code>\/root\/log<\/code> directory is present, so I have a place to store logs.<\/p>\n\n\n\n<p>I use the <code>$()<\/code> shell expansion to run <code>date<\/code> to print today\u2019s day of the month as a zero-padded number, so I can use the date as part of the log file\u2019s name when I run the <code>dnf<\/code> command to apply all updates. The last line reboots the system for me.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"automating-tasks-made-easy\">Automating tasks made easy<\/h2>\n\n\n\n<p>While job automation might sound like something that only system administrators do, it\u2019s a feature that <em>anyone<\/em> can use. If you need to do a task on a regular schedule, explore how you can use <strong>crontab<\/strong> to do the repetitive work for you.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you need to do a task on a regular schedule, explore how you can use crontab to do the repetitive work for you.<\/p>\n","protected":false},"author":33,"featured_media":3868,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[5,487,89],"tags":[104,91,97],"class_list":["post-6401","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","category-raspberry-pi","category-system-administration","tag-command-line","tag-linux","tag-sysadmin"],"modified_by":"David Both","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/6401","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6401"}],"version-history":[{"count":3,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/6401\/revisions"}],"predecessor-version":[{"id":6405,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/6401\/revisions\/6405"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/3868"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6401"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6401"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6401"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}